转载

Maven-Shade-Plugin打包SWT及JFace遇到的问题及解决方法

一、问题

当开启  m inimizeJar  后,精简的Jar运行会出现如下错误:

The image could not be loaded: FileImageDescriptor(location= class org.eclipse.jface.dialogs.TitleAreaDialog, name=images/title_banner.png)

org.eclipse.jface.resource.DeviceResourceException: Unable to create resource FileImageDescriptor(location= class org.eclipse.jface.dialogs.TitleAreaDialog, name=images/title_banner.png)

at org.eclipse.jface.resource.ImageDescriptor.createResource(ImageDescriptor.java:184)

at org.eclipse.jface.resource.DeviceResourceManager.allocate(DeviceResourceManager.java:55)

at org.eclipse.jface.resource.AbstractResourceManager.create(AbstractResourceManager.java:88)

at org.eclipse.jface.resource.ResourceManager.createImageWithDefault(ResourceManager.java:195)

…………

出现这个错误的直接现象是所有的图片——包括图标——都无法正确显示。

二、分析

开始以为是resources打包错误导致没有被压进jar包,不过分析jar包内容发现并没有文件缺失,然后开始分析minimizeJar的机制。文档上说:

<minimizeJar>

When  true , dependencies will be stripped down on the  class level to only the transitive hull required  for

the artifact. Note: Usage of 

this

feature requires Java 1.5 or higher.

由此来看,shade做的只是做了静态调用分析,并没有做动态类加载运行分析,因此极大可能问题是出在这里了。而且从抛出的异常来看,有可能是图片文件格式无法解析导致了图片资源创建失败。

那么之后的调试方法就简单了,首先上verbose大法,直接加verbose:class参数,分别运行正常的和异常的Jar包,从输出信息发现了端倪:

[Loaded java.lang.IndexOutOfBoundsException from C:/Program Files/Java/jre1.8.0_151/lib/rt.jar]

[Loaded java.lang.ArrayIndexOutOfBoundsException from C:/Program Files/Java/jre1.8.0_151/lib/rt.jar]

[Loaded org.eclipse.swt.internal.image.WinBMPFileFormat from file:/E:/Temp /**

***.jar]

[Loaded org.eclipse.swt.internal.image.GIFFileFormat from file:/E:/Temp/*****.jar]

[Loaded org.eclipse.swt.internal.image.WinICOFileFormat from file:/E:/Temp/*****.jar]

[Loaded org.eclipse.swt.internal.image.JPEGFileFormat from file:/E:/Temp/*****.jar]

[Loaded org.eclipse.swt.internal.image.JPEGSegment from file:/E:/Temp/*****.jar]

[Loaded org.eclipse.swt.internal.image.JPEGFixedSizeSegment from file:/E:/Temp/*****.jar]

[Loaded org.eclipse.swt.internal.image.JPEGStartOfImage from file:/E:/Temp/*****.jar]

[Loaded org.eclipse.swt.internal.image.PNGFileFormat from file:/E:/Temp/*****.jar]

[Loaded org.eclipse.swt.internal.image.PngInputStream from file:/E:/Temp/*****.jar]

[Loaded org.eclipse.swt.internal.image.PngDecodingDataStream from file:/E:/Temp/*****.jar]

[Loaded org.eclipse.swt.internal.image.PngChunkReader from file:/E:/Temp/*****.jar]

[Loaded org.eclipse.swt.internal.image.PngChunk from file:/E:/Temp/*****.jar]

[Loaded org.eclipse.swt.internal.image.PngIhdrChunk from file:/E:/Temp/*****.jar]

[Loaded org.eclipse.swt.internal.image.PngFileReadState from file:/E:/Temp/*****.jar]

[Loaded org.eclipse.swt.internal.image.PngPlteChunk from file:/E:/Temp/*****.jar]

[Loaded org.eclipse.swt.internal.image.PngIdatChunk from file:/E:/Temp/*****.jar]

[Loaded org.eclipse.swt.internal.image.PngIendChunk from file:/E:/Temp/*****.jar]

[Loaded org.eclipse.swt.internal.image.PngTrnsChunk from file:/E:/Temp/*****.jar]

[Loaded org.eclipse.swt.internal.win32.BITMAPINFOHEADER from file:/E:/Temp/*****.jar]

[Loaded org.eclipse.jface.window.ToolTip$ToolTipOwnerControlListener from file:/E:/Temp/*****.jar]

[Loaded org.eclipse.jface.window.ToolTip$$Lambda$19/1873653341 from org.eclipse.jface.window.ToolTip]

[Loaded org.cncert.xac.utils.ResourceManager from file:/E:/Temp/*****.jar]

[Loaded org.eclipse.swt.widgets.ToolItem from file:/E:/Temp/*****.jar]

[Loaded org.eclipse.swt.internal.win32.TBBUTTON from file:/E:/Temp/*****.jar]

[Loaded org.eclipse.jface.resource.AbstractResourceManager$RefCount from file:/E:/Temp/*****.jar]

[Loaded org.eclipse.swt.layout.FormData from file:/E:/Temp/*****.jar]

[Loaded org.eclipse.swt.layout.FormAttachment from file:/E:/Temp/*****.jar]

[Loaded org.eclipse.jface.dialogs.TitleAreaDialog$1 from file:/E:/Temp/*****.jar]

[Loaded org.eclipse.jface.window.ToolTip$TooltipHideListener from file:/E:/Temp/*****.jar]

注意上方红色的字体,在异常的Jar包输出中是没有的,同时检查了Jar包中果然没有打包这几个类!将这几个类手动加入Jar包后,异常的Jar包可以正常运行了。

从源代码看,这几个类型是SWT在运行时动态加载的,由 org.eclipse.swt.internal.image.FileFormat 动态加载,相关代码如下:

package org.eclipse.swt.internal.image;

import java.io.*;

import org.eclipse.swt.*;

import org.eclipse.swt.graphics.*;

/**

* Abstract factory class for loading/unloading images from files or streams

* in various image file formats.

*

*/

public abstract class FileFormat {

static final String FORMAT_PACKAGE = "org.eclipse.swt.internal.image";  // $NON-NLS-1$
static final String FORMAT_SUFFIX = "FileFormat";  // $NON-NLS-1$
static final String[] FORMATS = {"WinBMP", "WinBMP", "GIF", "WinICO", "JPEG", "PNG", "TIFF", "OS2BMP"};  // $NON-NLS-1$ // $NON-NLS-2$  // $NON-NLS-3$  // $NON-NLS-4$ // $NON-NLS-5$  // $NON-NLS-6$ // $NON-NLS-7$ // $NON-NLS-8$

LEDataInputStream inputStream;

LEDataOutputStream outputStream;

ImageLoader loader;

int compression;

static FileFormat getFileFormat (LEDataInputStream stream, String format)  throws Exception {

Class<?> clazz = Class.forName(FORMAT_PACKAGE + '.' + format + FORMAT_SUFFIX);

FileFormat fileFormat = (FileFormat) clazz.getDeclaredConstructor().newInstance();

if (fileFormat.isFileFormat(stream))  return fileFormat;

return null ;

}

三、Maven配置的修改

在官方文档中,可以使用filter来显式包含不想被minimizeJar优化掉的内容,但是目前filter还无法做到针对一个artifact中选择性对某个目录下的文件不做优化。

例如,我希望shade不对org/eclipse/swt/internal/image/**做优化,我的配置是这样的:

< filter >

< artifact > org.eclipse:swt.win32.x86_64 </ artifact >

< includes >

< include > org/eclipse/swt/internal/image/** </ include >

</ includes >

</ filter

>

但最终的结果是只打包了这个目录内的类,而这个artifact中的其他类全部被removed,因为 官方demo 中有这样一句话:

As of version 1.6, minimizeJar will respect classes that were specifically marked for inclusion in a filter. Note that  specifying an include filter for classes in an artifact implicitly excludes all non-specified classes in that artifact .

所以我只能让shade将swt的artifact全部保留,不做优化。

< filter >

< artifact > org.eclipse:swt.win32.x86_64 </ artifact >

< includes >

< include > ** </ include >

</ includes >

</ filter

>

或者使用artifactSet达到同样的效果。

四、总结

  1. 目前shade的机制是静态分析,所以可能会优化掉很多动态加载的类,对于使用动态加载较多的工程,在发布时需要特别注意。
  2. 目前shade的配置无法在一个artifact内显式指定某个路径不优化(或许有,但我确实没找到,欢迎知道的朋友留言告知),只能显式保留整个artifact不做优化。
无人分享的快乐不是真快乐,没人分担的痛苦是真痛苦。
原文  http://www.blogjava.net/feenn/archive/2019/04/29/433739.html
正文到此结束
Loading...