比较两个JAR的内容有时可能很有用。 在此博客文章中,我演示了一个Groovy脚本,该脚本的作用类似于比较两个JAR文件的简单“ diff”工具。 毫无疑问,此处显示的Groovy脚本jarDiff.groovy
可以进行改进,但确实可以达到我想要的效果。 该脚本通过以下方式比较两个提供的JAR:
- 显示两个JAR的路径,名称和大小,无论它们相同还是不同。
- 显示每个JAR中的其他JAR中不存在的条目
- 显示每个JAR中相同(按名称)但具有不同属性(CRC,大小或修改日期)的条目
脚本输出的上述特征意味着,对于相同的JAR,仅显示每个JAR的路径/文件名以及每个JAR的大小。 对于不同的JAR,将显示那些相同的属性以及仅存在于一个JAR中而不存在于另一个JAR中的条目,以及两个具有不同CRC,大小或修改日期的JAR之间共有的条目。 关于此脚本的一个重要区别是,它主要用于比较两个JAR中的元数据,并且在方法/ API级别(如javap之类的工具将提供)或源代码级别上不提供差异。 (将需要反编译器)。 该脚本确定存在差异,然后可以使用这些其他工具调查差异的更深层细节。
#!/usr/bin/env groovy
/**
* jarDiff.groovy
*
* jarDiff.groovy <first_jar_file> <second_jar_file>
*
* Script that compares to JAR files, reporting basic characteristics of each
* along with differences between the two JARs.
*/
if (args.length < 2)
{
println "\nUSAGE: jarDiff.groovy <first_jar_file> <second_jar_file>\n"
System.exit(-1)
}
TOTAL_WIDTH = 180
COLUMN_WIDTH = TOTAL_WIDTH / 2 - 3
ROW_SEPARATOR = "-".multiply(TOTAL_WIDTH)
import java.util.jar.JarFile
def file1Name = args[0]
def jar1File = new JarFile(file1Name)
def jar1 = extractJarContents(jar1File)
def file2Name = args[1]
def jar2File = new JarFile(file2Name)
def jar2 = extractJarContents(jar2File)
def entriesInJar1ButNotInJar2 = jar1.keySet() - jar2.keySet()
def entriesInJar2ButNotInJar1 = jar2.keySet() - jar1.keySet()
println ROW_SEPARATOR
println "| ${file1Name.center(COLUMN_WIDTH)} |${file2Name.center(COLUMN_WIDTH)} |"
print "| ${(Integer.toString(jar1File.size()) + " bytes").center(COLUMN_WIDTH)} |"
println "${(Integer.toString(jar2File.size()) + " bytes").center(COLUMN_WIDTH)} |"
println ROW_SEPARATOR
if (jar1File.manifest != jar2File.manifest)
{
def manifestPreStr = "# Manifest Entries: "
def manifest1Str = manifestPreStr + Integer.toString(jar1File.manifest.mainAttributes.size())
print "| ${manifest1Str.center(COLUMN_WIDTH)} |"
def manifest2Str = manifestPreStr + Integer.toString(jar2File.manifest.mainAttributes.size())
println "${manifest2Str.center(COLUMN_WIDTH)} |"
println ROW_SEPARATOR
}
entriesInJar1ButNotInJar2.each
{ entry1 ->
print "| ${entry1.center(COLUMN_WIDTH)} |"
println "${" ".center(entry1.size() > COLUMN_WIDTH ? 2 * COLUMN_WIDTH - entry1.size() : COLUMN_WIDTH)} |"
println ROW_SEPARATOR
}
entriesInJar2ButNotInJar1.each
{ entry2 ->
print "| ${" ".center(entry2.size() > COLUMN_WIDTH ? 2 * COLUMN_WIDTH - entry2.size() : COLUMN_WIDTH)}"
println "| ${entry2.center(COLUMN_WIDTH)} |"
println ROW_SEPARATOR
}
jar1.each
{ key, value ->
if (!entriesInJar1ButNotInJar2.contains(key))
{
def jar2Entry = jar2.get(key)
if (value != jar2Entry)
{
println "| ${key.center(COLUMN_WIDTH)} |${jar2Entry.name.center(COLUMN_WIDTH)} |"
if (value.crc != jar2Entry.crc)
{
def crc1Str = "CRC: ${value.crc}"
def crc2Str = "CRC: ${jar2Entry.crc}"
print "| ${crc1Str.center(COLUMN_WIDTH)} |"
println "${crc2Str.center(COLUMN_WIDTH)} |"
}
if (value.size != jar2Entry.size)
{
def size1Str = "${value.size} bytes"
def size2Str = "${jar2Entry.size} bytes"
print "| ${size1Str.center(COLUMN_WIDTH)} |"
println "${size2Str.center(COLUMN_WIDTH)} |"
}
if (value.time != jar2Entry.time)
{
def time1Str = "${new Date(value.time)}"
def time2Str = "${new Date(jar2Entry.time)}"
print "| ${time1Str.center(COLUMN_WIDTH)} |"
println "${time2Str.center(COLUMN_WIDTH)} |"
}
println ROW_SEPARATOR
}
}
}
/**
* Provide mapping of JAR entry names to characteristics about that JAR entry
* for the JAR indicated by the provided JAR file name.
*
* @param jarFile JAR file from which to extract contents.
* @return JAR entries and thir characteristics.
*/
def TreeMap<String, JarCharacteristics> extractJarContents(JarFile jarFile)
{
def jarContents = new TreeMap<String, JarCharacteristics>()
entries = jarFile.entries()
entries.each
{ entry->
jarContents.put(entry.name, new JarCharacteristics(entry.name, entry.crc, entry.size, entry.time));
}
return jarContents
}
像所有Groovy脚本一样,以上内容可以用Java编写,但是Groovy比Java更适合脚本编写。 上面的Groovy脚本利用了我在以前的博客文章中介绍的Groovy功能,例如Groovy的脚本报告 (用于格式化差异输出)和Groovy的Search JAR文件(用于阅读和读取JAR文件)。
此脚本有一些潜在的增强功能。 这些措施包括通过将一个清单文件的内容与另一个清单文件的内容进行比较,使脚本显示MANIFEST.MF文件中的差异,而不是JAR中所有文件中检测到的差异。 其他增强功能可能是通过使用反射来比较JAR中包含的类/接口/枚举上定义的方法。 但是,目前,我很满足于使用javap或javac -Xprint来查看上述脚本识别出特定类,枚举或接口中的差异之后的方法更改。
在各种情况下,能够快速识别两个JAR之间的差异可能是有益的,例如比较一个人自己生成的JAR的版本以进行更改,或者比较未命名的提供的库和框架的JAR(以使它们之间的差异显而易见的方式) 。 本文中演示的Groovy脚本识别了两个JAR之间的高级差异,同时展示了一些不错的Groovy功能。
翻译自: https://www.javacodegeeks.com/2013/07/comparing-jars-with-groovy.html