首页 > 其他分享 >用rust 写一个jar包 class冲突检测工具

用rust 写一个jar包 class冲突检测工具

时间:2023-05-23 23:34:16浏览次数:51  
标签:冲突检测 name zip jar let class rust

Rust很适合写命令行工具,特别是使用clap crate 更加方便,这篇文章介绍使用rust写一个jar包class冲突检测的工具。项目地址: https://github.com/Aitozi/jar_conflict_detector
首先jar包class冲突的现象是多个jar包中有同名的class,并且class的md5还不一样,那么就意味着该class存在多个版本,那么就存在冲突的可能。
思路比较简单,就是遍历每个jar包,记录ClassName 和 对应 CRC 校验码 及 jar 包的对应关系。
通过clap的derive api就可以快速定义个命令行的参数解析器。

#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
    #[arg(
        short,
        long = "jars",
        required = true,
        help = "The jar list joined by semicolon"
    )]
    jar_list: String,

    #[arg(long, help = "Disable the crc check", action = clap::ArgAction::SetTrue)]
    #[arg(default_value_t = false)]
    disable_crc: bool,

    #[arg(short, long, action = clap::ArgAction::Append, help = "The exclude package prefix")]
    exclude: Vec<String>,
}

通过zip读取jar包中的entry, 过滤只处理.class文件,并从zip_file中读取crc32的元数据,这样可以避免读取原始数据生成md5,可以大大加快处理速度。
中间编写的时候遇到了一个常见的rust borrow checker的问题。
以下代码为例

fn main() {
    let path = "/tmp/a.jar";
    let jar = File::open(path).unwrap();
    let mut zip = ZipArchive::new(jar).unwrap();

    for name in zip.file_names() {
        let entry = zip.by_name(name);
        println!("name: {}, size: {}", name, entry.unwrap().size());
    }
}

我是想通过遍历ZipArchive#file_names然后根据文件名获取ZipFile但是会有如下编译错误
image.png

pub fn file_names(&self) -> impl Iterator<Item = &str> {
    self.shared.names_map.keys().map(|s| s.as_str())
}
/// Search for a file entry by name
pub fn by_name<'a>(&'a mut self, name: &str) -> ZipResult<ZipFile<'a>> {
    Ok(self.by_name_with_optional_password(name, None)?.unwrap())
}

但是用以下的方式就没有问题

let path = "/tmp/a.jar";
let jar = File::open(path).unwrap();
let mut zip = ZipArchive::new(jar).unwrap();

for i in 0..zip.len() {
    let entry = zip.by_index(i).unwrap();
    println!("name: {}, size: {}", entry.name(), entry.size());
}

这里我比较奇怪的是从方法签名上看 len()file_names()都会发生immutable borrow,而后面by_indexby_name都会发生mutable borrow。为什么会一个可以通过检查,一个不行。

pub fn len(&self) -> usize {
    self.shared.files.len()
}

len函数实际的签名应该是fn len<'a>(&'a self) -> usize 返回值是usize,所以函数调用完成后就不再和借用有关了。所以 immutable borrow 就结束了。
file_names实际签名是fn file_names<'a>(&'a self) -> impl Iterator<Item = &'a str> {…}返回值的生命周期和 入参的 immutable ref周期相同,所以后续就检测出同时存在可变和不可变引用了。
详细解释: https://users.rust-lang.org/t/borrow-check-understanding/94260/2

命令行频繁被Killed问题

问题现象是当使用cargo build打包出binary后,通过cp 到 /tmp/jcd执行 会出现 Killed的情况,不是必现,但是当出现之后后续就一直会这样,百思不得其解。

$ /tmp/jcd
[1]    16957 killed     /tmp/jcd

后通过在rust user 论坛提问找到答案,不得不说回复效率很高。
https://users.rust-lang.org/t/rust-command-line-tools-keeps-beeing-killed/94179
原因应该是和苹果电脑上的 Code sign机制有关,在苹果没有解决这个问题之前,建议通过ditto替代cp命令来copy程序。
经过检查系统日志确实有出现 Code Signature Invalid的报错
image.png

相同的Class CRC和MD5却不一样

问题是发现在集成这个工具到内部的插件框架中,集成过程中发现一个Jar包被另一个module依赖,经过shade插件打包(没有对相关class进行relocate) 后,生成的class crc32不同,被识别为会冲突的类。通过javap -v 查看两个class对比发现里面的仅仅是一些constant pool 不同。
那么怀疑就是maven-shade-plugin 做了什么操作,翻阅了下代码,查看了shade的处理流程.
看到以下这段,发现这不就是我遇到的问题么。
image.png
查阅了相应的issue: https://issues.apache.org/jira/browse/MSHADE-391
在3.3.0 才解决,而我使用的版本正好是3.2.4。升级插件重新生成校验码一致了。

解决冲突的Class

最后再回到最初的目的,当我们通过工具检测出冲突的class应该怎么解决呢。
首先我们需要判断这个class是否是运行时所需要的。
如果不是所需要的那么我们就应该直接排掉他,排除有两种手段(这里针对的是maven shade的打包方式),如果在dependency tree中可以看到相应package的依赖,那么可以直接通过如下的白名单 include 或者 exclude 掉某个 artifact。

<artifactSet combine.self="override">
  <includes>
    <include>commons-dbcp:commons-dbcp</include>
    <include>commons-pool:commons-pool</include>
    <include>mysql:mysql-connector-java</include>
  </includes>
</artifactSet>

但是不排除这个依赖包本身就是fatjar,那么直接通过这种方式就排不掉这个依赖,可以通过filters 配置文件 粒度的匹配过滤

<filters>
  <filter>
    <artifact>*:*</artifact>
    <excludes>
      <exclude>META-INF/*.SF</exclude>
      <exclude>META-INF/*.DSA</exclude>
      <exclude>META-INF/*.RSA</exclude>
      <exclude>javax/**</exclude>
      <exclude>org/apache/flink/fnexecution/**</exclude>
      <exclde>org/slf4j/**</exclde>
    </excludes>
  </filter>
</filters>

如果这个冲突的class是运行时需要的,那么可以通过relocation的方式给各自的插件包中shade成带特殊前缀的class名,解决同名冲突。

<relocation>
    <pattern>org.apache.http</pattern>
    <shadedPattern>com.alipay.flink.sls.shaded.org.apache.http</shadedPattern>
</relocation>

标签:冲突检测,name,zip,jar,let,class,rust
From: https://www.cnblogs.com/Aitozi/p/17426768.html

相关文章

  • Paper Reading: forgeNet a graph deep neural network model using tree-based ensem
    目录研究动机文章贡献本文方法图嵌入深度前馈网络forgeNet特征重要性评估具体实现模拟实验合成数据生成实验评估实验结果真实数据应用BRCA数据集microRNA数据Healthyhumanmetabolomics数据集优点和创新点PaperReading是从个人角度进行的一些总结分享,受到个人关注点的侧重......
  • [Java]instanceof和getClass()的区别
    getClass()willbeusefulwhenyouwanttomakesureyourinstanceisNOTasubclassoftheclassyouarecomparingwith. classA{}classBextendsA{}Objecto1=newA();Objecto2=newB();o1instanceofA=>trueo1instanceofB=>false......
  • 小爱同学调用本地jar -巴法云
    本文通过巴发云注册mqtt服务并在小爱同学注册实现为什么要用巴法云的自然是因为他免费啦1.准备1.1注册巴法云https://cloud.bemfa.com/1.2在巴法云上创建mqtt设备云注意主题名称一开始最好是006结尾不是数字小爱是不会识别的点击昵称就可以直接修改并且昵称和......
  • Class类
    1. 基本介绍  7151. Class也是类,因此也继承Object类 [类图]2. Class类对象不是new出来的,而是系统创建的3.对于某个类的Class类对象,在内存中只有一份, 因为类只加载一次4.每个类的实例都会记得自己是由哪个Class实例所生成5.通过Class对象可以完整地得到一个类的完整结构,通过......
  • UE4 UCLASS(...)
    类说明符UCLASS和元数据(meta)可以定义引擎和编辑器特定的行为。类声明的语法如下所示:UCLASS([specifier,specifier,...],[meta(key=value,key=value,...)])classClassName:publicParentName{GENERATED_BODY()}声明包含一个类的标准C++类声明。在标准声......
  • 解决启动jar包报错:错误找不到或无法加载主类 jar
    错误:找不到或无法加载主类jar问题描述:在使用springboot框架对项目打包后,手动使用命令java-jar包名启动jar包,报错:错误:找不到或无法加载主类jar。网上找了各办法,都是加maven插件,打成可执行jar包<build><plugins><plugin><groupId>org.springframework.bo......
  • idea中jdk11用maven编译失败 Fatal error compiling: tools.jar not found: XX\Java
    ideamaven编译需要用到jdk的lib包里面的tools.jar文件,但是jdk1.8之后就没有tools.jar了。我这里用的是graalvm的jdk11,编译一直报错,网上也查不到。解决办法: 根据对应路径创建一个lib包,并把jdk1.8的lib下面的tools.jar复制一个放到这个包下面,让这个路径有这个包就行了。我......
  • 【cpluscplus教程翻译】Classes (II)
    操作符重载本质上,类就是在C++代码里定义了新的类型,在代码中,类型不仅用来构造和赋值,还可以用操作符进行运算,考虑基础类型的加减乘除inta,b,c;a=b+c;上面这个例子用了加法操作符和赋值操作符,对于基础类型,这些操作的含义非常显而易见且无歧义,但是对自定义类型来说,不见得......
  • 以指定编码方式启动jar
    UTF-8编译的Java应用程序,在Windows运行,指定文件编码为UTF-8,控制台显示的中文仍乱码的解决方式。首先查看当前代码页的编码方式: 方式一:右键cmd控制台>属性,可以看到当前代码页为:936(简体中文GBK),该代码只能显示GBK编码的中文。 方式二:直接在控制台输入chcp......
  • ISay obj=(ISay)Class.forName(args[0]).newInstance(); obj.say();
    classmainClass{publicstaticvoidmain(String[]args){System.out.println(args[0]);try{ISayobj=(ISay)Class.forName(args[0]).newInstance();obj.say();}catch(InstantiationExceptione){//TODOAuto-generatedcatch......