豆角茄子子

导航

jar包的License信息都存在于哪块

最近两天在搞产品发布前的jar包License扫描工作,因为这个数据交换项目是基于Kettle做的二次开发,而Kettle本身是一款强大的开源数据交换产品,其中包含了近两千个jar包,所以为了产品发布后不被惹上官司,需要扫描介质里所有jar包的License。

那么问题来了,如何扫描介质里所有jar包的License呢

大概了解pom.xml文件的小伙伴可能有想法了,直接去<licenses>标签获取即可。

另外,熟悉Maven公共库网站https://mvnrepository.com/的小伙伴可能也有想法了,可以写段Java程序,拼接url(https:/mvnrepository.com/artifact/groupId/artifactId/version)在线获取jar包信息Html页面,再去解析即可。

那么url标红的变量从哪获取呢?对于绝大部分小伙伴来说,项目开发就在外网,所以直接去遍历扫描Maven仓库目录即可轻松获取到(当然,这种也有局限性,就是这个jar包必须是Maven编译出来的,而非Ant);但是对于像我们这种在公司内网开发的来说,领导最多能帮你把介质放到外网就不错了,所以你拥有的只有jar包本身,要想获取以上三个变量,需要解析pom.xml文件(这种也有同样的局限性),这里建议最好使用Maven官方的API工具类MavenXpp3Reader来准确解析(maven-model-3.6.0.jar),以免自己手动解析xml出现各种问题,特别是需要扫描很多jar包的时候。一个好的Java程序员应该尝试熟悉使用各种外部工具,毕竟Java流行的原因很大程度上得益于Java开源社区的强大支持。实现代码如下:

public static boolean searchInPom(MyFileEntity fe) {
	JarFile jf = null;
	try {
		jf = new JarFile(fe.getFile());
		Enumeration<?> entries = jf.entries();
		while (entries.hasMoreElements()) {
			JarEntry entry = (JarEntry) entries.nextElement();
			if (entry.getName().toLowerCase().endsWith("pom.xml")) {
				StringWriter writer = new StringWriter();
				try {
					org.springframework.util.FileCopyUtils.copy(new InputStreamReader(jf.getInputStream(entry)),
							writer);
				} catch (IOException e1) {
					e1.printStackTrace();
				}
				StringReader sr = new StringReader(writer.toString());
				MavenXpp3Reader reader = new MavenXpp3Reader();
				Model model = null;
				try {
					model = reader.read(sr);
					List<License> licenses = model.getLicenses();
					if (licenses != null && licenses.size() > 0) {
						// System.out.println(licenses.get(0).getName());
						fe.setLicense(licenses.get(0).getName());
						return true;
					} else {
						// pom中没有license标签,则尝试联网获取
						String groupId = model.getGroupId();
						String artifactId = model.getArtifactId();
						String version = model.getVersion();
						Parent parent = model.getParent();
						String groupId2 = null;
						String artifactId2 = null;
						String version2 = null;
						if (parent != null) {
							groupId2 = parent.getGroupId();
							artifactId2 = parent.getArtifactId();
							version2 = parent.getVersion();
						}
						// 自定义的fe对象简单封装了这三个变量groupId,artifactId和version
						fe.setGroupId(groupId == null ? groupId2 : groupId);
						fe.setArtifactId(artifactId == null ? artifactId2 : artifactId);
						fe.setVersion(version == null ? version2 : version);
						// System.out.println(fe);
						return searchOnInet(fe); // searchOnInet方法自己实现
					}
				} catch (IOException | XmlPullParserException e) {
					e.printStackTrace();
				} finally {
					if (sr != null) {
						sr.close();
					}
				}
			}
		}
	} catch (IOException e2) {
		e2.printStackTrace();
	} finally {
		try {
			if (jf != null) {
				jf.close();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	return false;
}

其实,jar包的License信息的存放是有千奇百怪的规范或方式,那接下来总结一下哪些相对具体的地方都有可能放置License吧。

一.META-INF/maven/**/pom.xml

具体位置1:Licenses元素

条件:Maven编译出来的jar,且要含有此标签,有的pom中无此标签(如slf4j-api-1.6.1.jar)

示例:mssql-jdbc.jar

具体位置2:一般在pom开头的注释

条件:Maven编译出来的jar,且要含有相关注释,有的pom中无此注释(如slf4j-api-1.6.1.jar)

示例:commons-io.jar

二.META-INF/MANIFEST.MF

具体位置:Bundle-License:***

条件:含有MANIFEST.MF文件,有的无此文件(如jmi.jar),且含有此key,有的无此key(如asm.jar)

示例:mssql-jdbc.jar

补充获取License实现代码(需要准确获取MANIFEST中的License信息,可以使用jdk的类java.util.jar.Manifest):

public static final String MANIFEST_ENTRY = "META-INF/MANIFEST.MF";
static String LICENSE_TAG = "Bundle-License";
public static boolean searchInMf(MyFileEntity fe) {
	JarFile jf = null;
	FileInputStream fis = null;
	try {
		jf = new JarFile(fe.getFile());
		JarEntry entry = jf.getJarEntry(MANIFEST_ENTRY);
		if (entry != null) {
			Manifest m = jf.getManifest();
			Attributes ma = m.getMainAttributes();
			String url = ma.getValue(LICENSE_TAG);
			if (url != null) {
				// System.out.println(url);
				fe.setUrl(url);
				return true;
			}
		}
	} catch (IOException e) {
		e.printStackTrace();
	} finally {
		try {
			if (fis != null) {
				jf.close();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	return false;
}

三.META-INF/LICENSE或META-INF/LICENSE.txt(有的在META-INF下,有的不在)

具体位置:一般存在于第一二行文本

条件:META-INF下含有此文件,有的无此文件(如mssql-jdbc.jar),有的有此文件,但打开无License信息(如dom4j.jar)

示例:apache-log4j-extras.jar

四.META-INF/NOTICE或META-INF/NOTICE.txt

具体位置:License文本信息位置不固定

条件:META-INF下含有此文件,有的无此文件(如guava.jar)

示例:fastjson.jar

五.其他

具体位置:about.html或其他各种文件

条件:任何文件都有可能,因为任何文件可以被人为写入任何信息包括license

示例1:jface.jar(about.html)

示例2:ftp4che.jar(COPYING文件)

示例3:ftp4che.jar(类注释)

示例4:bcprov-jdk14.jar(LICENSE.java)

posted on 2019-01-29 18:09  豆角茄子子  阅读(775)  评论(0编辑  收藏  举报