Jarの中のJarを読みたい

  • MyApp.jar
    • /META-INF
      • MANIFEST.MF
    • Hello.jar
      • /myapp/example
        • Hello.class
        • Hello.properties

といった構成で、MyApp.jar内のHello.jarが持っているHello.classとかpropertiesを読み出したい。

コード


出力例

./build/libs/MyApp.jar
- example.jar
-- myapp/  0
0x00000000 |                         | 
-- myapp/example/  0
0x00000000 |                         | 
-- myapp/example/Hello.class  733
0x00000000 | CA FE BA BE 00 00 00 34 | .......4
......
-- myapp/example/Hello.properties  19
0x00000000 | 6D 65 73 73 61 67 65 3D | message=
......

わかったこと

コード中に書いてないけども、URLからopenStreamはできなかった。
Jarは new URL("jar:file://..../MyApp.jar!/example.jar") とすると、getContentでストリームが取れたりするけども、 new URL("jar:file://..../MyApp.jar!/example.jar!/myapp/example/Hello.class") といった感じにJarが入れ子になっているとダメ。

パッと見るとJarEntryからデータを取れないようにみえるが、大本のJarFileが持つgetInputStreamに渡せば取ることが出来る。
が、Jarの中のJarだとJarFileのインスタンスが得られないので困った。(JarEntryが示すファイルがJarファイル以外もあるのだから当然だけど。)

ファイルのストリームは取り出せるので、それをJarInputStreamに横流しにする。
JarInputStream は getNextJarEntry とすると、JarEntryに合わせてストリームの開始位置が変化する。
なので、JarInputStream#getNextJarEntry したあと、JarInputStream#read すればちゃんとJarEntryが保持する情報を取り出せる。

入れ子になっている場合でも、JarInputStream自体を再度JarInputStreamに横流し・・・といった感じに再帰的にやれば、入れ子になったJarの中身も全て読み出せる。はず。