Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JDiagram older version throwing StackOverflowError with JRE 8 at ExtendedArrayList.sort

I'm using JDiagram JAR like below

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

This code works fine when run with JRE 7 however when it is run with JRE 8, following error is being thrown:

java.lang.StackOverflowError
    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)

I followed the stack trace to JDiagram decompiled code. Observed that routeAllLinks() calls RouteLinks() on another object (say router) and at one more level deep ExtendedArrayList.sort() which is appeared in error stack trace is called. The "ExtendedArrayList" in JDiagram extends ArrayList and contains a method named "sort()" which has following definition.

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

On Google I found out that JRE 8 has introduced List.sort() and delegates the Collections.sort() calls to collection's (ExtendedArrayList in my case) sort method. And so library ExtendedArrayList.sort() became an override. And it creates an infinite recursion which results in stackoverflow. I could reproduce this issue even with small piece of code as well now.

Also

  • Our original class which creates JDiagram object, is being loaded at runtime by some other component in our product. We have very little control over the loading of our program.
  • We have found out that latest version of JDiagram has fixed this issue by replacing sort() with sortJ7() method. However, we cannot upgrade the library at this moment. JDiagram is a licensed API.
  • ExtendedArrayList is being instantiated by JDiagram internally and so we cannot alter it from our code.

We have tried following solutions which didn't work so far

  • Java Proxy: Because our code does not call ExtendedArrayList directly and also 'Diagram' does not have any interface.
  • Spring AOP: We are not using spring and also our program is loaded runtime by other component.
  • AspectJ: By now, this was apparently a solution. However, it also didn't work as we are not able to weave our program at runtime. Not sure if someone could make it work.

Kindly let me know if any point needs elaboration. Any help is welcome. Thanks.

UPDATE So far, javassist is the best approach however there JDiagram obfuscation is preventing the solution to work correctly. We have kind of assumed that it is impossible (have to say) to fix considering our release date on our head. We have started process to upgrade library. And meanwhile removed a small feature from our application which was being provided by routeAllLinks() method.. :-( thanks everyone for your help. I'll be continuing my research on this issue as I found it really intriguing and challenging.. I'll update the post if I could resolve it.. And I'll be giving bounty to @gontard for his javassist approach as I'm continuing my research with it. Thanks.

like image 560
Rahul Winner Avatar asked Mar 16 '15 03:03

Rahul Winner


2 Answers

I have reproduced your problem with a basic example:

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.StackOverflowError
    }
}

I was able to bypass the java.lang.StackOverflowError by renaming the method using javassist:

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]
    }
}

I have not tried with your version of JDiagram because I get only the last (Java 8 compatible) version on their website.

like image 79
gontard Avatar answered Sep 20 '22 09:09

gontard


Think about decompiling the library and fixing the issue by yourself. You could use this fixed package as a workaround.

An alternative would be, to place a fixed version of the class in your code. Same package like in the library and same class name of course:

com.mindfusion.common.ExtendedArrayList

Maybe you have to configure the classloader to load your class instead of looking up the faulty class in the library first. Options like "parent first" or simply accessing the class from your code once before calling the library could make the deal.

like image 39
Peter Wippermann Avatar answered Sep 22 '22 09:09

Peter Wippermann