きっかけと疑問:
-
Class.getResource()
やClassLoader.getResourceAsStream()
はピンポイントでリソースのフルパスを指定する。 - → ファイルシステムのディレクトリのように、リソースを再帰的に読みだすのはどうすればよいか?
結論から書くと、CodeSourceを使ってclasspathのファイル・システムまたはjarファイルにアクセスすることで実現できるので、参考までにメモ。
java.security.CodeSource
-
https://docs.oracle.com/javase/jp/8/docs/api/java/security/CodeSource.html
-
https://docs.oracle.com/javase/jp/8/docs/technotes/guides/security/spec/security-spec.doc3.html
- "3.2 java.security.CodeSource" 参照
-
CodeSourceは、jarファイルやファイルシステム上のクラスファイルなどのコード位置をカプセル化している。
-
CodeSource.getLocation()
でコード位置をURLとして取得できる。
-
-
ローカルから
java -jar xxx.jar
で起動した場合も、*.class
のあるディレクトリをclasspathに入れて起動した場合も、いずれもURLのスキーマはfile:
となる。 -
よって、fileスキーマのURLから
URL.getFile()
を取得し、ディレクトリであれば通常のファイルシステムとしてアクセスできる。- jarファイルであれば
java.util.jar.JarFile
を使ってjarファイルに格納されたファイルにアクセスできる。
- jarファイルであれば
-
あるクラスが格納されているCodeSourceを取得するには、
Class.getProtectionDomain().getCodeSource()
を用いる。- Javaのセキュリティシステムが有効な場合にパーミッションチェックの対象となる。
- https://docs.oracle.com/javase/jp/8/docs/api/java/security/ProtectionDomain.html
-
https://docs.oracle.com/javase/jp/8/docs/technotes/guides/security/spec/security-spec.doc4.html
- "4 アクセス制御メカニズムとアルゴリズム" 参照
サンプルコード
-
https://github.com/msakamoto-sf/java-codesource-sample
- Java SE 8u74(x64)にてWin10/CentOS7.2で確認
- Linux系でビルド&実行するときは
build_run_linux.sh
を実行、Windowsならbuild_run_win.bat
を実行。
Linuxでの実行結果:
===> execute testpkg.Main from filesystem
Main : file
Main : /home/msakamoto/github/msakamoto-sf/java-codesource-sample/main/
(...)
Foo : file:/home/msakamoto/github/msakamoto-sf/java-codesource-sample/libfoo/
(...)
Bar : file:/home/msakamoto/github/msakamoto-sf/java-codesource-sample/libbar/
===> execute testpkg.Main from jar
Main : file
Main : /home/msakamoto/github/msakamoto-sf/java-codesource-sample/main.jar
(...)
Foo : file:/home/msakamoto/github/msakamoto-sf/java-codesource-sample/libfoo.jar
(...)
Bar : file:/home/msakamoto/github/msakamoto-sf/java-codesource-sample/libbar.jar
Windowsでの実行結果:
"===> execute testpkg.Main from filesystem"
Main : file
Main : /C:/work/SVNWORK/github/msakamoto-sf/java-codesource-sample/main/
(...)
Foo : file:/C:/work/SVNWORK/github/msakamoto-sf/java-codesource-sample/libfoo/
(...)
Bar : file:/C:/work/SVNWORK/github/msakamoto-sf/java-codesource-sample/libbar/
(...)
"===> execute testpkg.Main from jar"
Main : file
Main : /C:/work/SVNWORK/github/msakamoto-sf/java-codesource-sample/main.jar
(...)
Foo : file:/C:/work/SVNWORK/github/msakamoto-sf/java-codesource-sample/libfoo.jar
(...)
Bar : file:/C:/work/SVNWORK/github/msakamoto-sf/java-codesource-sample/libbar.jar
(...)
サンプルコードでの実験結果サマリ:
- ファイルシステム上のmain, libfoo, libbarディレクトリをクラスパスに指定した時は、
CodeSource.getLocation()
がそれぞれのディレクトリを返した。 - jarファイルをクラスパスに指定した時は、
CodeSource.getLocation()
がそれぞれのファイルシステム上のjarファイル名を返した。 - いずれの場合も、Java標準のAPIを使ってファイルシステム/jarファイル内のコンテンツ一覧にアクセスできた。
参考URL
- JAVAセキュリティメカニズムのメモ - seraphyの日記
- 自作Javaアプリにサンドボックスで動くアドインの仕組みを作る方法 - seraphyの日記
- PathMatchingResourcePatternResolver (Spring Framework API 2.5)
- Recursive Resource Gathering in Java - CodeProject
感想:
- 普段サーバサイドのJavaを扱うことが多いため気づかないことが多いが、Java自体はクライアント・アプリケーションとしての歴史の方が長い。
- そのため、サーバサイドでは普段意識しないようなセキュリティ系の機能が、意外とキー要素になっていたりする。
- デプロイ機能やWebStartなど、サーバサイドでは普段使わないようなJavaの機能に、意外とお宝が眠ってるかもしれない。