Замените класс в библиотеке классов Java пользовательской версией

класс BasicLabelUI на пакета javax/качели/plafбыл/основные влияет подтвердили ошибку. В моем приложении I нужно функциональные возможности исправлена версия (подана для v9). По юридическим и техническим причинам я по-прежнему привязан к затронутой версии JDK.

мой подход состоял в том, чтобы создать пакет пакета javax/качели/plafбыл/основные внутри моего проекта, содержащего фиксированный версия.

Как я могу заставить свой проект отдать предпочтение моей включенной версии класса над дефектным классом в установленном JDK?

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

2 ответов


как упоминалось в других ответах, вы мог бы в теории, конечно, распакуйте JVM в rt.jar file и замените файл совместимой версией с исправленными ошибками.

любые классы библиотеки классов Java, такие как Swing, загружаются загрузчик класса начальной загрузки который ищет свои классы от этого rt.jar. Вы можете вообще не добавить классы в этот classpath без добавления их в этот файл. Существует (нестандартная) опция VM

-Xbootclasspath/jarWithPatchedClass.jar:path

где вы добавляете файл jar, который включает исправленную версию, но это не обязательно работает на любой виртуальной машине Java. Кроме того, это незаконное развертывание приложения это меняет это hehavior! Как указано в официальная документация:

не развертывайте приложения, использующие этот параметр для переопределения класса в rt.jar потому что это нарушает Java Runtime Двоичный код среды лицензия.

если вы, однако, добавили класс к загрузчику класса начальной загрузки (что возможно без использования нестандартных API с помощью API инструментария), среда выполнения все равно загрузит исходный класс в качестве загрузчика класса начальной загрузки в этом случае ищет rt.jar первый. Поэтому невозможно "затенить" сломанный класс без изменения этого файла.

наконец, это всегда незаконно распространять ВМ с пропатчен файл, т. е. поставив его в производственную систему для клиента. В лицензионном соглашении четко указано, что вам нужно

[...] распространяйте [Java runtime] полный и немодифицированный и только в комплекте как часть ваших апплетов и приложений

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

конечно, вы можете в теории создайте свою собственную версию OpenJDK, но вы не можете вызвать двоичный Java больше, когда вы распространяете его, и я предполагаю, что ваш клиент не позволит этого то, что вы предлагаете в своем ответе. По опыту, многие безопасные среды вычисляют хэши двоичных файлов перед выполнением, что запрещает оба подхода настройки выполняющейся виртуальной машины.

самым простым решением для вас, вероятно, будет создание Java agent что вы добавляете в ваш процесс VM при запуске. В конце концов, это очень похоже на добавление библиотеки в качестве зависимостей пути к классу:

java -javaagent:bugFixAgent.jar -jar myApp.jar

агент Java способен заменить двоичное представление класса при запуске приложения и поэтому может изменить реализацию метода багги.

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

public static class BugFixAgent {
  public static void premain(String args, Instrumentation inst) {
    inst.addClassFileTransformer(new ClassFileTransformer() {
      @Override
      public byte[] transform(ClassLoader loader, 
                              String className, 
                              Class<?> classBeingRedefined, 
                              ProtectionDomain protectionDomain, 
                              byte[] classfileBuffer) {
        if (className.equals("javax/swing/plaf/basic/BasicLabelUI")) {
          return patchedClassFile; // as found in the repository
          // Consider removing the transformer for future class loading
        } else {
          return null; // skips instrumentation for other classes
        }
      }
    });
  }
}

в Javadoc java.lang.instrumentation пакет предлагает подробное описание того, как построить и реализовать агент Java. Используя этот подход, вы можете использовать фиксированную версию рассматриваемого класса без нарушения лицензионного соглашения.

из опыта, агенты Java являются отличным способом для исправления временных ошибок в сторонних библиотеках и в библиотеке классов Java без необходимости развертывания изменений в коде или даже требуется развернуть новую версию для клиента. На самом деле, это типичный случай использования агент Java.


Как я могу заставить свой проект отдать предпочтение моей включенной версии класса над дефектным классом в установленном JDK?

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

предполагая, что вы можете определить соответствующую версию в исходных репозиториях OpenJDK, можно создать свой собственный вкус библиотек Java с исправленной ошибкой. Однако этого не будет. настоящая Java. Конечно, это не будет квалифицироваться как" затронутая версия Java", которую вы вынуждены использовать. (И, кроме того, вы обязуетесь бесконечный цикл повторного применения вашего патча к каждому новому выпуску патча текущей версии Java ...)

теоретически также возможно поместить модифицированную версию некоторого стандартного класса библиотеки Java в JAR и добавить ее в путь к классу начальной загрузки JVM с помощью командная строка. Но это равносильно изменению "затронутая версия Java" тоже.

выполнение этого с помощью агента Java для использования исправленной версии класса также нарушает правила. И это сложнее.


Если вы и ваши клиенты решите, что настройка JVM является приемлемым решением, то делать это через путь к классу начальной загрузки, вероятно, самый простой и чистый подход. И это определенно законно1.

тем не менее, я бы рекомендовал вам найти обходной путь для ошибка до версии Java 9 с вашего исправления.


1 - На самом деле, даже подход сборки из измененного источника является законным, потому что двоичная лицензия Oracle не применяется к этому. Двоичная лицензия предназначена для распространения измененной версии двоичного файла Oracle. Другая возможная проблема заключается в том, что вы можете нарушать условия использования торговой марки Java, если вы распространяете версию, несовместимую с "истинной" Java, и называете свой дистрибутив "Java". Решение в этом ... Не называйте это "Java"!

однако, не просто следуйте моим советам. Спросите адвоката. А еще лучше, не делай этого вообще. Это излишне сложно.