web-dev-qa-db-ja.com

Javaクラスライブラリ内のクラスをカスタムバージョンに置き換える

javax/swing/plaf/basicのクラスBasicLabelUIは、 a 確認済みのバグ 。私のアプリケーションでは、need機能が fixed version(filed for v9) によって提供されています。法的および技術的な理由により、私は影響を受けるJDKバージョンに拘束されます。

私のアプローチは、プロジェクト内に修正バージョンを含むパッケージjavax/swing/plaf/basicを作成することでした。

インストールされたJDKの欠陥のあるクラスよりも自分のインクルードされたバージョンのクラスを優先するようにプロジェクトを強制するにはどうすればよいですか?

固定クラスも顧客側で機能している必要があり、JDKインストールの欠陥のあるクラスは無視する必要があるため、これはある程度移植可能でなければなりません。したがって、私はJDKを変更するのではなく、この特定のクラスをバイパスします。

21
bogus

他の回答で述べたように、理論的には、JVMのrt.jarファイルを可能です解凍して、互換性のあるバグ修正済みのバージョンで置き換えます。

SwingのようなJavaクラスライブラリのクラスは、このrt.jarからクラスを検索するbootstrapクラスローダーによってロードされます。このファイルにクラスを追加せずに、通常、このクラスパスにプリペンドクラスを追加することはできません。 (非標準)VMオプションがあります

-Xbootclasspath/jarWithPatchedClass.jar:path

パッチを適用したバージョンを含むjarファイルを先頭に追加しますが、これはどのJava仮想マシンでも機能するとは限りません。また、この動作を変更するアプリケーションを配備することは不正です公式ドキュメント に記載されているように:

Javaランタイム環境のバイナリコードライセンスに違反するため、このオプションを使用してrt.jarのクラスをオーバーライドするアプリケーションをデプロイしないでください。

ただし、bootstrapクラスローダーにクラスを追加した場合(計装APIを使用して非標準APIを使用せずに可能なこと)、ランタイムは引き続きbootstrapクラスローダーとして元のクラスをロードしますこの場合、最初にrt.jarを検索します。したがって、このファイルを変更せずに、壊れたクラスを「シャドウイング」することは不可能です。

最後に、常に VMをパッチされたファイルとともに配布する 、つまり、それを顧客の本番システムに配置することは違法です。使用許諾契約には、

[...] [Javaランタイム]を完全に変更せずに配布し、アプレットおよびアプリケーションの一部としてのみバンドルする

したがって、配布するVMを変更することはお勧めできません。これが明らかになると、法的な結果に直面する可能性があります。

もちろん、理論的には独自のバージョンのOpenJDKを構築することはできますが、配布時にバイナリJavaを呼び出すことはできず、顧客はこれを許可しないと思いますあなたはあなたの答えで提案します。経験上、多くの安全な環境では、実行前にバイナリのハッシュを計算して、実行中のVMを調整する両方のアプローチを禁止します。

最も簡単な解決策は、起動時にVMプロセスに追加する Javaエージェント を作成することでしょう。結局のところ、これはクラスパスの依存関係としてライブラリを追加することと非常に似ています。

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エージェントを使用する典型的な使用例です。

20

インストールされたJDKの欠陥のあるクラスよりも自分のインクルードされたバージョンのクラスを優先するようにプロジェクトを強制するにはどうすればよいですか?

簡単な答え-できません。少なくとも、影響を受けるJavsバージョンを使用するという制約を厳守している間はそうではありません。

OpenJDKソースリポジトリで適切なバージョンを識別できると想定すると、バグにパッチを適用して、独自のJavaライブラリを構築できます。ただし、これは実際のJavaではありません。確かに、使用が制限されている「影響を受けるJavaバージョン」とは見なされません。 (それに加えて、現在のバージョンのJava ...の新しい各パッチリリースにパッチを再適用する無限のサイクルに自分をコミットしています。)

理論的には、一部のJava標準ライブラリクラスの変更されたバージョンをJARに入れ、_-Xbootclasspath_コマンドラインオプションを使用して、JVMのbootstrapクラスパスの前に追加することもできます。 。しかし、これは「影響を受けるJavaバージョン」も変更することと同じです。

Javaエージェントを使用してパッチを適用したバージョンのクラスを使用すると、ルールも破られます。そして、それはより複雑です。


あなたとあなたの顧客がJVMの微調整が許容できる解決策であると決定した場合、bootstrapクラスパスを介してそれを行うことがおそらく最も簡単で最もクリーンなアプローチです。そしてそれは間違いなく合法です1

ただし、修正されたJava 9のバージョンがリリースされるまで、バグの回避策を見つけることをお勧めします。


1-実際には、Oracle Binaryライセンスが適用されないため、build-from-modified-sourceアプローチでさえ合法です。 Binaryライセンスは、修正されたバージョンのOracleバイナリを配布するためのものです。他の考えられる問題は、「真の」Javaと互換性のないバージョンを配布し、ディストリビューションを「Java」と呼ぶ場合、Java商標の使用に関する規約に違反している可能性があることです。それに対する解決策は...それを「Java」と呼ばないでください!

ただし、私のアドバイスに従うだけではいけません。弁護士に相談してください。いっそのこと、まったくそれをしないでください。不必要に複雑です。

0
Stephen C