最近两天在搞产品发布前的jar包License扫描工作,因为这个数据交换项目是基于Kettle做的二次开发,而Kettle本身是一款强大的开源数据交换产品,其中包含了近两千个jar包,所以为了产品发布后不被惹上官司,需要扫描介质里所有jar包的License。
那么问题来了,如何扫描介质里所有jar包的License呢?
大概了解pom.xml文件的小伙伴可能有想法了,直接去
另外,熟悉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 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)