java - 执行时加载资源的问题

标签 java build cross-platform swt

这是潜在问题的背景,我正在与一个使用 Swt 库的项目组合作,我正在尝试打包软件以进行部署。事实证明,SWT 非常依赖于平台/体系结构。我希望能够将所有六个 jar(linux、mac、win 和 32/64 位)打包到同一个包中,并根据系统使用适当的库。我意识到这是一个艰巨的挑战,但是现在切换到 Swing(或其他任何工具)并不是一个真正的选择。

我发现了许多相关主题(@Aaron Digulla's thread@mchr's thread),它们为我提供了有关手头问题的宝贵见解。我已尝试实现@Alexey Romanov here 提出的解决方案.有一个区别,因为他提出的 loadSwtJar() 方法不是静态的,所以我实例化对象,紧接着,在对对象执行任何其他操作之前运行该方法。

它显示为加载程序无法正常工作。我对此声明的推理如下:

  • 如果所有 Swt jar 都从可执行 jar 文件的类路径中删除,则 Exception in thread "main"java.lang.NoClassDefFoundError: org/eclipse/swt/events/MouseListener 被抛出,这是由以下原因引起的:java.lang.ClassNotFoundException: org.eclipse.swt.events.MouseListener

对我来说,这意味着在类路径中找不到这些库,我错了吗?

  • 如果 swt jar 留在类路径中,则系统在执行期间使用第一个 jar 文件。这意味着如果 gtk-linux-x86_64 恰好是 jar 列表中的第一个 swt jar,那么系统会尝试使用它,无论系统是 win32 还是 Mac OSX。

我尝试添加一些输出以查看 loadSwtJar() 方法是否选择了正确的 jar,并且输出似乎在我尝试过的所有平台上都是正确的,因为选择了正确的包(并且文件确实存在于可运行的 jar 中)。但是仍然没有加载正确的库,因此会发生执行错误: exception in thread "main"java.lang.reflect.InvocationTargetException 由 for ex: Caused by: java.lang.UnsatisfiedLinkError: Cannot load 32-bit SWT libraries on 64-bit JVM (请注意,如果我更改 build.xml 文件中 64 位和 32 位 swt 库的出现顺序,这是我在 Linux 机器上遇到的错误)

那么,这里的问题似乎是什么?我是否遗漏了一些细节,或者根本不可能检查系统属性并相应地加载适当的库?

下面是我的构建文件的摘录,认为它可能有助于找到问题的根源。

提前致谢


编辑: 在与同事进行长时间的调试 session 后,问题得到解决(除了我提到的关于 MacOS 线程管理的恼人错误 here )。它涉及调整 ANT 构建以及编写主类的方式。 (事实证明,主类正在扩展和实现来自 SWT 库的引用,这意味着代码根本无法编译,用另一个类包装主类并从那里加载 SWT jar,这似乎就足够了解决问题)

感谢并问候所有做出贡献的人,尤其是@Aaron。非常感谢!

最佳答案

这是我的主类的最新版本的副本。让我知道这是否适合你。我在 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);
        }
    }
}

关于java - 执行时加载资源的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3959556/

相关文章:

java - 如何保持代码整洁有序?

git pull origin master,不 pull 检查是否有冲突

macos - Mac 上 Electron 中的快捷方式

java - 将字符串作为参数从一个 Java 应用程序传递到另一个

java - 使用 Comparator 对对象列表进行排序

java - 如何设置一些像Animation-List一样重复的字符串?

build - 为什么Dart的Polymer包不包含builder.dart?

multithreading - Maven 多线程构建如何工作?

python - python跨平台高精度时间

c# - C#程序可以跨平台吗?