Проблемы с загрузкой ресурсов во время исполнения - PullRequest
5 голосов
/ 18 октября 2010

Вот фон основной проблемы: я сотрудничаю с группой в проекте, который использует библиотеки Swt, и я пытаюсь упаковать программное обеспечение для развертывания. Оказывается, SWT очень зависит от платформы / архитектуры. Я хотел бы иметь возможность упаковать все шесть jar (linux, mac, win и 32/64-bit) в один пакет и использовать соответствующую библиотеку в зависимости от системы. Я понимаю, что это сложная задача, однако переключение на Swing (или что-то еще) на самом деле не вариант сейчас.

Я нашел несколько соответствующих тем ( @ нить Аарона Дигуллы и @ mchr ), которые предоставили мне ценную информацию о рассматриваемой проблеме. Я попытался реализовать решение, предложенное @Alexey Romanov здесь . С одним отличием, поскольку предлагаемый им метод loadSwtJar() не является статичным, я создаю экземпляр объекта и сразу после этого запускаю метод, прежде чем что-либо еще будет сделано с объектом.

Похоже, что процедура загрузки не работает должным образом. Мое объяснение этому утверждению следующее:

  • Если все jar-файлы Swt удалены из пути к классам исполняемого файла jar, выдается Exception in thread "main" java.lang.NoClassDefFoundError: org/eclipse/swt/events/MouseListener, что вызвано: java.lang.ClassNotFoundException: org.eclipse.swt.events.MouseListener

для меня это означает, что библиотеки не найдены в classpath, я ошибаюсь?

  • Если на пути к классам оставлено swt jar, то первый файл jar используется системой во время выполнения. Это означает, что если gtk-linux-x86_64 окажется первым jar-файлом swt в списке jar-файлов, то система попытается использовать его независимо от того, установлена ​​ли на нем система win32 или Mac OSX.

Я попытался добавить некоторые выходные данные, чтобы увидеть, выбирает ли метод loadSwtJar() правильный jar, и вывод выглядит правильным на всех платформах, которые я пробовал, так как выбран правильный пакет (и файлы существуют в бегущая банка). Но тем не менее правильная библиотека не загружена, поэтому возникают ошибки выполнения: Exception in thread "main" java.lang.reflect.InvocationTargetException вызвано, например: Caused by: java.lang.UnsatisfiedLinkError: Cannot load 32-bit SWT libraries on 64-bit JVM (Обратите внимание, что это ошибка, которая появляется на моем компьютере с Linux, если я изменяю порядок появления 64-битных и 32-битных библиотек SWT в файле build.xml)

Итак, в чем здесь проблема? Я упускаю некоторые детали или просто невозможно проверить системные свойства и загрузить соответствующую библиотеку соответственно?

Наконец, ниже приводится выдержка из моего файла сборки, и я подумал, что это может помочь найти источник проблемы.

Заранее спасибо,


РЕДАКТИРОВАТЬ: После продолжительного сеанса отладки с коллегой проблема устранена (за исключением досадной ошибки, связанной с управлением потоками в MacOS, как я уже упоминал здесь ). Он включал в себя настройку сборки ANT, а также способ написания основного класса. (Как оказалось, основной класс расширял и реализовывал ссылки из библиотеки SWT, что означало, что код не будет компилироваться вообще, оборачивал основной класс другим классом и загружал оттуда файлы SWT, которых, по-видимому, было достаточно. для решения проблемы)

Спасибо и привет всем, кто внес свой вклад, особенно @Aaron. Очень ценю!

Ответы [ 2 ]

5 голосов
/ 18 октября 2010

Вот копия последней версии моего Основного класса. Дайте мне знать, если это работает для вас. Я тестировал его на Linux (32/64-бит) и Windows (32-бит).

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 голосов
/ 18 октября 2010

Вы можете использовать Java Web Start в качестве механизма начальной загрузки для вашего мультиплатформенного SWT-приложения. См. соответствующую запись в SWT FAQ.

В качестве альтернативы, вы можете поместить собственные библиотеки SWT для каждой платформы в отдельные папки и указать их -Djava.library.path в вашем сценарии запуска для конкретной платформы.

...