在执行期间加载资源的问题

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

我找到了许多相关主题( @Aaron Digulla的主题和@ 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

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

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

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

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

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

提前致谢,


编辑:经过与同事的长时间调试会议后,问题得到解决(除了我在这里提到的关于MacOS上的线程管理的烦人的错误)。 它涉及到使用ANT构建进行调整以及编写主类的方式。 (事实certificate,主类是从SWT库扩展和实现引用,这意味着代码根本不会编译,用另一个类包装主类并从那里加载SWT jar似乎已经足够了解决这个问题)

感谢所有贡献的人,特别是@Aaron。 非常感谢!

这是我的Main类的最新版本的副本。 如果这对您有用,请告诉我。 我在Linux(32 / 64bit)和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); } } } 

您可以使用Java Web Start作为多平台SWT应用程序的引导机制。 请参阅SWT FAQ中的相应条目 。

或者,您可以将每个平台的SWT本机库放入单独的文件夹中,并在特定于平台的启动脚本中指定它们-Djava.library.path。