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