JDiagram 旧版本在 ExtendedArrayList.sort 中使用 JRE 8 抛出 ***Error

Posted

技术标签:

【中文标题】JDiagram 旧版本在 ExtendedArrayList.sort 中使用 JRE 8 抛出 ***Error【英文标题】:JDiagram older version throwing ***Error with JRE 8 at ExtendedArrayList.sort 【发布时间】:2015-05-18 03:02:39 【问题描述】:

我正在使用如下所示的 JDiagram JAR

Diagram myDigram = new Diagram();
    myDigram.routeAllLinks();

此代码在使用 JRE 7 运行时可以正常工作,但是在使用 JRE 8 运行时,会引发以下错误:

java.lang.***Error
    at java.util.Collections.sort(Unknown Source)
    at com.mindfusion.common.ExtendedArrayList.sort(Unknown Source)
    at java.util.Collections.sort(Unknown Source)
    at com.mindfusion.common.ExtendedArrayList.sort(Unknown Source)
    at java.util.Collections.sort(Unknown Source)
    at com.mindfusion.common.ExtendedArrayList.sort(Unknown Source)

我跟踪堆栈跟踪到 JDiagram 反编译代码。观察到 routeAllLinks() 在另一个对象(例如路由器)上调用 RouteLinks() 并且在更深的一层上调用了出现在错误堆栈跟踪中的 ExtendedArrayList.sort()。 JDiagram 中的“ExtendedArrayList”扩展了 ArrayList 并包含一个名为“sort()”的方法,其定义如下。

  public void sort(Comparator<? super T> paramComparator)
  
    Collections.sort(this, paramComparator);
  

在 Google 上,我发现 JRE 8 引入了 List.sort() 并将 Collections.sort() 调用委托给集合的(在我的例子中为 ExtendedArrayList)排序方法。所以库 ExtendedArrayList.sort() 变成了一个覆盖。它创建了一个无限递归,导致***。我现在也可以用一小段代码重现这个问题。

还有

我们创建 JDiagram 对象的原始类在运行时由我们产品中的其他一些组件加载。我们对程序的加载几乎没有控制权。 我们发现最新版本的 JDiagram 已通过将 sort() 替换为 sortJ7() 方法来解决此问题。但是,我们目前无法升级库。 JDiagram 是一个获得许可的 API。 ExtendedArrayList 正在由 JDiagram 在内部实例化,因此我们无法从代码中更改它。

我们已经尝试了以下解决方案,但到目前为止都不起作用

Java Proxy:因为我们的代码没有直接调用 ExtendedArrayList 而且“图表”也没有任何界面。 Spring AOP:我们是 不使用弹簧,我们的程序也被其他人加载运行时 零件。 AspectJ:到目前为止,这显然是一个解决方案。然而, 它也不起作用,因为我们无法在 运行。不确定是否有人可以使它工作。

如果有任何需要详细说明,请告诉我。 欢迎任何帮助。谢谢。

更新 到目前为止,javassist 是最好的方法,但是 JDiagram 混淆会阻止解决方案正常工作。考虑到我们的发布日期,我们有点假设不可能(不得不说)修复。我们已经开始升级库的过程。同时从我们的应用程序中删除了一个由 routeAllLinks() 方法提供的小功能.. :-( 感谢大家的帮助。我将继续我对这个问题的研究,因为我发现它真的很有趣和具有挑战性。如果我能解决它,我会更新这篇文章。我将赏金 @gontard 的 javassist 方法,因为我我继续我的研究。谢谢。

【问题讨论】:

哪个代码在调用sort,您的应用程序还是库? Holger,感谢您的帮助。库 (JDiagram) 正在内部调用 sort() 方法。由于 JAVA 8 在各个集合类中引入了 sort() 方法并将 Collections.sort() 委托给 arraylist 排序方法,因此库 sort() 成为覆盖并导致递归......希望它澄清...... 非技术选项:请 JDiagram 的人在您使用的版本中使用 sortJ7 方法重新编译他们的代码。 Assylias:如果可能的话,我不会在这里发布问题! JDiagram 是一个许可的 api,获得补丁或升级需要更多时间。我们即将发布,没有时间去完成所有那些获得升级的合法程序。顺便说一下,我们已经开始与经理讨论,以便我们可以升级到我们的下一个版本。如果您有任何其他解决方案,请提出..谢谢.. 在您获得更新的 JDiagram 库之前,我会犹豫是否使用 Java 8。如果 Java 8 中确实存在某些问题,那么可能还有更多的问题。此外,如果这是一个许可的专有库,任何类型的 hacky 方法,甚至你所做的反编译都可能违反他们的许可。 【参考方案1】:

我已经用一个基本的例子重现了你的问题:

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

public class ExtendedArrayList<E> extends ArrayList<E> 
    @Override
    public void sort(Comparator<? super E> c) 
        Collections.sort(this, c);
    


import java.util.Arrays;

public class Main 
    public static void main(String[] args) throws Exception 
        ExtendedArrayList<String> arrayList = new ExtendedArrayList<String>();
        arrayList.addAll(Arrays.asList("z", "y", "x"));
        arrayList.sort(String::compareTo); // -> java.lang.***Error
    

我能够通过使用javassist 重命名方法来绕过java.lang.***Error

import java.util.Arrays;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;

public class Main 
    public static void main(String[] args) throws Exception 
        ClassPool pool = ClassPool.getDefault();
        CtClass ctClass = pool.get("ExtendedArrayList");
        CtClass[] sortParams = new CtClass[] pool.get("java.util.Comparator");
        CtMethod sortMethod = ctClass.getDeclaredMethod("sort", sortParams);
        sortMethod.setName("sortV7"); // rename
        ctClass.toClass();

        ExtendedArrayList<String> arrayList = new ExtendedArrayList<String>();
        arrayList.addAll(Arrays.asList("z", "y", "x"));
        System.err.println(arrayList); // print [z, y, x]
        arrayList.sort(String::compareTo);
        System.err.println(arrayList); // print [x, y, z]
    

我没有尝试过您的JDiagram 版本,因为我在他们的网站上只获得了最后一个(Java 8 兼容)版本。

【讨论】:

谢谢.. 我还没有尝试过 javassist.. 你能否告诉我它是否也使用一些 javaagent 或类似于 AspectJ weaving 的东西,其中涉及自定义类加载。因为正如我所提到的,我们的类是在运行时加载的。此外,ExtendedArrayList 是由 JDiagram 而不是我的代码实例化的。同时我会尝试你的代码并更新线程。再次感谢!! 还有一个疑问,Javassist 是否也适用于混淆 JAR? Javassist 无需任何代理即可运行,它只是一个库。我不知道混淆是否会改变行为。 我能够在具有自己的 main() 方法的示例程序中修复 ***。但是,我的项目应用程序引发以下异常: javassist.NotFoundException: com.mindfusion.common.ExtendedArrayList at javassist.ClassPool.get(ClassPool.java:450) 显然这是因为我们的项目应用程序加载类的方式。正如我在问题陈述中已经提到的,AspectJ 对我不起作用,因为我们在运行时由另一个组件加载我们的类。不过,我发现 Javassist 是最好的方法,因为它不涉及任何 javagent.thx 我仍然在玩 Javassist 并尝试我的运气。非常感谢。【参考方案2】:

考虑反编译库并自己解决问题。您可以使用此固定包作为解决方法。

一种替代方法在您的代码中放置一个固定版本的类。与库中相同的包和相同的类名:

com.mindfusion.common.ExtendedArrayList

也许你必须配置类加载器来加载你的类,而不是先在库中查找有问题的类。诸如“父优先”之类的选项或在调用库之前仅从代码中访问一次类就可以达成交易。

【讨论】:

谢谢彼得,但是这是一个许可库,它会被认为是违规行为..此外,JAR 被混淆了,所以 sort() 的所有引用都不能被替换.. 好的,拉胡尔,我明白了。但是第二种选择(在您的代码库中用固定版本替换类)仍然是一个有效的选择吗?您必须找出类的混淆名称,然后将其放入您的代码中。【参考方案3】:

我看到你的程序的加载不是由你控制的。 执行以下步骤:

    包括可以将"sort" 方法名称更改为可以避免覆盖排序方法的任何其他名称的程序的jar(javassist)

    通过反射class.forName("");在程序的main方法开头加载jar的(javassist)主类

    调用 jar 的 (javassist) 方法以对方法执行所需的更改

这样您就可以确保任何 jar (javassist) 都已加载并可以使用。

【讨论】:

不确定 class.forName() 是否可以替换已经加载的类。到目前为止,javassist 是最好的方法,但是 JDiagram 混淆会阻止解决方案正常工作。 我理解您的回答,但没有详细说明我的评论。我可以在我的程序中获取 javassist jar 类。但是使用 javassist 或任何此类 jar,我无法拦截 JDiagram 类,因为它被混淆了,我们无法控制 JDiagram 的加载方式。 如果是授权的jar,说明你已经付费了,那么他们必须提供支持,我想如果你问他们并解释他们的情况,他们会根据情况更改代码满足您的需求。 我要求您在发表评论之前彻底阅读我的问题、其他答案和 cmets。非常感谢您的帮助。 我已经阅读了你的问题,所以如果你正在升级 jar,那么你的方法排序已经改变了,所以你不需要 javassist,只是说,不需要研究那部分再浪费你的时间。

以上是关于JDiagram 旧版本在 ExtendedArrayList.sort 中使用 JRE 8 抛出 ***Error的主要内容,如果未能解决你的问题,请参考以下文章

Github wiki 在显示旧版本

在centos6下面,需怎么彻底删除旧版本的erlang

无法在 macOS Sierra 上运行旧版本的 Xcode

iTunes Connect 旧版本/版本的分发

在 Azure KeyVault 中禁用旧版本的机密

在centos6下面,需怎么彻底删除旧版本的erlang