URLStreamHandler
というカスタムを登録して、Amazon S3 URLへのリクエストを一般的な方法で処理しようとしています。ハンドラの実装は、S3-URLStreamHandler (github)のようになります。 Handler
クラスをsun.net.www.protocol.s3
パッケージに入れませんでしたが、カスタムパッケージcom.github.dpr.protocol.s3
を使用しました。 Javaパッケージをこのパッケージに取り込むために、URL classのドキュメントに続いて、システムプロパティ-Djava.protocol.handler.pkgs="com.github.dpr.protocol"
を提供しました。しかし、私はs3://my.bucket/some-awesome-file.txt
のようなS3-URLを処理しようとした場合、私はMalformedURLException
を得る:Spring Webアプリケーション(Tomcat)でカスタムURLStreamHandlerを登録します
Caused by: java.net.MalformedURLException: unknown protocol: s3
at java.net.URL.<init>(URL.java:600)
at java.net.URL.<init>(URL.java:490)
at java.net.URL.<init>(URL.java:439)
at java.net.URI.toURL(URI.java:1089)
...
私のアプリケーションは、現在のTomcatで動作します春ベースのWebアプリケーション、ですが、およそあらゆる知識と雑然としてはなりません基本となるアプリケーションコンテナ
それぞれのコードを既にデバッグしていて、クラスをロードするために使用されたクラスローダーがそれを知らないため、URLStreamHandler
を初期化できないことがわかりました。これはjava.net.URL
からそれぞれのコード(JDK 1.8.0_92)である:
1174: try {
1175: cls = Class.forName(clsName);
1176: } catch (ClassNotFoundException e) {
1177: ClassLoader cl = ClassLoader.getSystemClassLoader();
1178: if (cl != null) {
1179: cls = cl.loadClass(clsName);
1180: }
1181: }
(Class.forName
によって使用される)java.net.URL
クラスのクラスローダは、ブートストラップクラスローダと認識していないシステムクラスローダを示すnull
ある私クラス。ブレークポイントを入れて、現在のスレッドのクラスローダーを使用してハンドラクラスをロードしようとすると、正常に動作します。つまり、私のクラスは明らかにそこにあり、アプリケーションのクラスパスにありますが、Javaはハンドラーをルックアップするために「間違った」クラスローダーを使います。
私はthis question on SOFを認識してんだけど、Tomcatは、それはアプリケーションの起動時に自社工場(org.apache.naming.resources.DirContextURLStreamHandlerFactory
)だと、単一のJVMに登録さ1つの工場が存在しなければなら登録するように私は、カスタムURLStreamHandlerFactory
を登録してはいけません。 TomcatのDirContextURLStreamHandlerFactory
はカスタムプロトコルの処理に使用できるユーザーファクトリを追加することができますが、アプリケーションを別のコンテナで実行する必要があるため、アプリケーションコード内のTomcatに依存関係を追加したくありません。
カスタムURLのハンドラをコンテナに依存しない方法で登録する方法はありますか?
UPDATE 2017年1月25日:
オプション1 - このオプションは、として働く反射
を使用してカスタムURLStreamHandlerFactoryのセットを:
私は別のオプション@Nicolas Filottoは試して提案しました期待される。しかし、リフレクションを使用することによって、java.net.URL
クラスの内部動作に厳密な依存関係が導入されます。幸いなことに、Oracleは基本的なJavaクラスの利便性の問題を解決することにあまり熱心ではありません。実際にはほぼ14年(素晴らしい仕事Sun/Oracle)のためオープンしています。
オプション2 - {JAVA_HOME}/JRE/libに/ extに
このオプションは、同様に動作へのハンドラのJARを置きます。しかし、システム拡張としてハンドラjarを追加するだけでは、トリックはできません。ハンドラのすべての依存関係も追加する必要があります。これらのクラスは、このJavaインストールを使用するすべてのアプリケーションで表示されるため、クラスパス内の同じライブラリのバージョンが異なるため、望ましくない影響が発生する可能性があります。
オプション3 - {CATALINA_HOME}/libに
にハンドラJARを入れてこれは動作しません。 Tomcat's classloader documentationによると、このディレクトリに置かれるリソースは、Tomcatのコモンクラスローダーを使用してロードされます。このクラスローダーは、java.net.URL
によってプロトコルハンドラをルックアップするために使用されません。
これらのオプションが指定されている場合は、反射バリアントを使用します。すべてのオプションはそれほど素晴らしいものではありませんが、少なくとも最初のものは簡単にテストすることができ、デプロイロジックを必要としません。
public static void registerFactory() throws Exception {
final Field factoryField = URL.class.getDeclaredField("factory");
factoryField.setAccessible(true);
final Field lockField = URL.class.getDeclaredField("streamHandlerLock");
lockField.setAccessible(true);
// use same lock as in java.net.URL.setURLStreamHandlerFactory
synchronized (lockField.get(null)) {
final URLStreamHandlerFactory urlStreamHandlerFactory = (URLStreamHandlerFactory) factoryField.get(null);
// Reset the value to prevent Error due to a factory already defined
factoryField.set(null, null);
URL.setURLStreamHandlerFactory(new AmazonS3UrlStreamHandlerFactory(urlStreamHandlerFactory));
}
}
CL問題の場合は、 'S3-URLStreamHandler'を専用のjarファイルに入れて、' tomcat/lib'または '$ {JAVA-HOME}/jre/lib/ext'にデプロイしようとしましたか? ?このようにして、あなたのクラスは見える階層内で十分に高いCLになります。 –
@NicolasFilotto実際には、これがアプリケーションコード自体で解決できることを期待していました。つまり、ハンドラクラスを別の場所に配置しようとはしませんでした。もし別の可能性があると思われなければ、私は試してみましょう。 – dpr
これは、アプリケーションコードのTomcatクラスに依存関係を導入します。実際には、異なるアプリケーションサーバーで同じコードを区別する必要なく実行できるように、私が避けたいものです。 – dpr