Подтвердить что ты не робот

Старая версия JDiagram бросает StackOverflowError с JRE 8 в ExtendedArrayList.sort

Я использую JDiagram JAR, как показано ниже

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

Этот код отлично работает при запуске с JRE 7, однако, когда он запускается с JRE 8, возникает следующая ошибка:

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)

Я следил за трассировкой стека до декомпилированного кода JDiagram. Наблюдалось, что routeAllLinks() вызывает RouteLinks() на другом объекте (скажем, маршрутизаторе) и на еще одном уровне deep ExtendedArrayList.sort(), который появляется в трассировке стека ошибок. "ExtendedArrayList" в JDiagram расширяет ArrayList и содержит метод с именем "sort()", который имеет следующее определение.

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

В Google я узнал, что JRE 8 представила List.sort() и делегирует вызовы Collections.sort() в метод сортировки (ExtendedArrayList в моем случае). И поэтому библиотека ExtendedArrayList.sort() стала переопределенной. И это создает бесконечную рекурсию, которая приводит к stackoverflow. Я мог бы воспроизвести эту проблему даже с небольшим фрагментом кода, а теперь.

Кроме

  • Наш оригинальный класс, который создает объект JDiagram, загружается во время выполнения другим компонентом нашего продукта. Мы очень мало контролируем загрузку нашей программы.
  • Мы выяснили, что последняя версия JDiagram исправила эту проблему, заменив метод sort() на sortJ7(). Однако в настоящий момент мы не можем обновить библиотеку. JDiagram - лицензированный API.
  • ExtendedArrayList создается внутри JDiagram внутри, поэтому мы не можем изменить его из нашего кода.

Мы пробовали следующие решения, которые не работали до сих пор

  • Java Proxy: поскольку наш код не вызывает ExtendedArrayList напрямую а также "Диаграмма" не имеет интерфейса.
  • Spring АОП: мы не используя spring, а также наша программа загружается во время выполнения другими компонент.
  • AspectJ: К настоящему времени это было, по-видимому, решением. Однако, это также не сработало, поскольку мы не можем во время выполнения. Не уверен, что кто-то может заставить его работать.

Пожалуйста, дайте мне знать, если какая-либо точка нуждается в разработке. Любая помощь приветствуется. Спасибо.

UPDATE До сих пор javassist - лучший подход, однако там, где запутывание JDiagram препятствует правильному функционированию решения. Мы предположили, что невозможно (надо сказать) исправить нашу дату выпуска на нашей голове. Мы начали процесс обновления библиотеки. А между тем удалили небольшую функцию из нашего приложения, которая предоставлялась методом routeAllLinks().:-( Спасибо всем за вашу помощь. Я продолжу свое исследование по этому вопросу, поскольку считаю, что это действительно интригует и бросает вызов. Я обновляю сообщение, если я смогу его решить. И я дам награду @gontard за его джавассистский подход, Продолжая мои исследования с этим. Спасибо.

4b9b3361

Ответ 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.StackOverflowError
    }
}

Мне удалось обойти java.lang.StackOverflowError, переименовав метод, используя 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]
    }
}

Я не пробовал вашу версию JDiagram, потому что на своем веб-сайте я получил только последнюю (совместимую с Java 8) версию.

Ответ 2

Подумайте о декомпиляции библиотеки и исправлении проблемы самостоятельно. Вы можете использовать этот фиксированный пакет в качестве обходного пути.

Альтернативным будет разместить фиксированную версию класса в коде. Тот же пакет, что и в библиотеке, и его имя, конечно же:

com.mindfusion.common.ExtendedArrayList

Возможно, вам нужно настроить загрузчик классов для загрузки своего класса, а не сначала искать неисправный класс в библиотеке. Такие параметры, как "родительский первый" или просто доступ к классу из вашего кода один раз перед вызовом библиотеки, могут сделать сделку.

Ответ 3

Я вижу, что загрузка вашей программы не контролируется вами. Выполните следующие шаги:

  • Включить jar (javassist) программы, которая может изменить имя метода "sort" на любое другое имя, которое могло бы избежать переопределения метода сортировки

  • Загрузите основной класс jar's (javassist) путем отражения class.forName(""); в начале основного метода вашей программы.

  • Вызвать метод jar's (javassist) для выполнения необходимых изменений в методах

Таким образом, вы можете быть уверены, что всякая jar (javassist) загружена и готова к использованию.