jarファイルの中身を、解凍せずに変更する方法メモ

## はじめに
Javaのプロジェクトの仕事の中で、ソースコードをjarファイルとしてビルドをして、それをサーバーにデプロイしていました。そのデプロイされたjarファイルの中身をテスト用に書き換えたくなった時に、少し嵌ったのでここに解決方法をメモしておきます。

尚、本記事ではSpringフレームワークのプロジェクトを例として扱います。jarファイルの解凍時のディレクトリは、以下のようになっています。

```
┣ BOOT-INF/
  ┣ classes
    ┗ application.properties <- *このファイルを書き換えたい*
  ┗ lib
    ┗ {使用してるライブラリのjar}
┣ META-INF/
  ┣ maven/
    ┗ {pom.xmlとか}
  ┗ MANIFEST.MF　<- マニフェストファイル
┗ org/
  ┗ {classファイルがいっぱい}
```

具体的には、`/BOOT-INF/classes/application.properties`を書き換えようとして、失敗しました。
## 失敗例
### 解凍してから、zipコマンドで固める
jarファイルは実質的にはzipファイルと変わらないので、`zip`コマンドでjarファイルを解凍して、ファイルを修正し、再度`zip`コマンドで固め直そうとしました。

この結果できたファイルを基にアプリを`java -jar`コマンドで起動しようとすると、以下のエラーが出ました。

```
Exception in thread "main" java.lang.IllegalStateException: Failed to get nested archive for entry BOOT-INF/lib/bcpkix-jdk15on-1.60.jar
        at org.springframework.boot.loader.archive.JarFileArchive.getNestedArchive(JarFileArchive.java:108)
        at org.springframework.boot.loader.archive.JarFileArchive.getNestedArchives(JarFileArchive.java:86)
        at org.springframework.boot.loader.ExecutableArchiveLauncher.getClassPathArchives(ExecutableArchiveLauncher.java:70)
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:49)
        at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:51)
Caused by: java.io.IOException: Unable to open nested jar file 'BOOT-INF/lib/bcpkix-jdk15on-1.60.jar'
        at org.springframework.boot.loader.jar.JarFile.getNestedJarFile(JarFile.java:256)
        at org.springframework.boot.loader.jar.JarFile.getNestedJarFile(JarFile.java:241)
        at org.springframework.boot.loader.archive.JarFileArchive.getNestedArchive(JarFileArchive.java:103)
        ... 4 more
Caused by: java.lang.IllegalStateException: Unable to open nested entry 'BOOT-INF/lib/bcpkix-jdk15on-1.60.jar'. It has been compressed and nested jar files must be stored without compression. Please check the mechanism used to create your executable jar file
        at org.springframework.boot.loader.jar.JarFile.createJarFileFromFileEntry(JarFile.java:284)
        at org.springframework.boot.loader.jar.JarFile.createJarFileFromEntry(JarFile.java:264)
        at org.springframework.boot.loader.jar.JarFile.getNestedJarFile(JarFile.java:252)
        ... 6 more
```

エラーメッセージに`nested jar files must be stored without compression`とある通り、`BOOT-INF/lib`以下のライブラリのjarファイル群を圧縮してしまったことで(?)うまくjarファイルとして読み込まれなかったようです。

### 解凍してから、jarコマンドで再作成
単純なzipコマンドでダメなら、jarコマンドでjarファイルの作成を試みてみます。

```bash
jar cvfm xxx.jar ./META-INF/MANIFEST.MF ./
```
上のコマンドで作成したjarファイルを基に起動しようとすると、以下のエラーが出ました。

```
no main manifest attribute, in xxx.jar
```
ちゃんとオプションでマニフェストファイルを指定しているのに、上手く読み込まれていないようです（この現象の直接的な原因は、未解決です）。
## 成功例
### zipコマンドで、解凍せずに中身を入れ替える
jarファイルを解凍せずに中身を書き換えることで、上記の問題をクリアしました。

まず、作業しているローカルの場所で、書き換えたいファイルとそれが置かれているディレクトリを再現します。
私は今回`BOOT-INF/classes/application.properties`というファイルを書き換えたかったので、それを作ります。

```bash
mkdir -p BOOT-INF/classes && touch ./BOOT-INF/classes/application.properties
```
このローカルの`application.properties`ファイルを書き換えたら、それをzipに取り込みます。
取り込みたいzipファイル(`xxx.jar`)を同ディレクトリに持ってきて、以下のコマンドで取り込みます。

```bash
zip ./xxx.jar ./BOOT-INF/classes/MANIFEST.MF
```

この結果できたjarファイルは、一度も解凍してないためかエラーが起こらず、問題なく動かすことができました。

## ついでに
上に記したのは、jarファイルの中身を書き換える方法でした。
中身を書き換えるのではなく、ただ見るだけであれば、以下のコマンドで見れます。

```bash
jar tf xxx.jar
```

中身を確認するためにわざわざjarファイルを解凍していたら、上司に怒られました。