はじめに
今まで本Webサイトの管理人たるpandaがScalaで書いたプログラムは、データを標準入力から読み込む実装とはせず、そのかわりにファイルに対応するInputStreamインスタンスを作成してファイルに記述されているデータを読み込み、必要な処理を行っていました。
このたび、そんな状況から一歩踏み出して、Windows 10 Homeで標準入力から読み込んだデータを1行ごとに正規表現を使って処理し、その結果を標準出力に書き出すプログラムを書くことにしました。
このプログラムはコマンドラインでのみ使用することを想定していたため、sbt-assemblyでFat JARファイルを作ってみたところ…
いろいろとハマりました!! (´・ω・`)
最初は原因の見当がつかずにいろいろと試行錯誤しましたが、この記事では最も解決に手間取ってしまった「Fat JARファイルにあるはずのScalaのクラスファイルがなかった問題」について書きます。
書いたコードについて。
まず、実際に書いたコードについて書きます。
Windows 10 Home に Emacs 26.2 をインストールし、最近いい感じらしいと巷で評判の Metals をインストールしてScalaのプロジェクトを作成後、IDEっぽいコード補完の恩恵に与りつつ、以下のコードを書きました。なお、クラス名はいまいちアレな感じなのは見逃していただく方向でお願いいたします。
MetalsのWebサイトの記述はLinuxへのインストールを前提としたものとなっている箇所が多々あり、特にファイルのパスについては読み替えが必要ですが、Metalsのサーバについては Windows 10 Home へインストールできました。😀
Fat JARファイルの作成に失敗しているように見える件。
上記のコードを含むプロジェクト作成して適切にbuild.sbt等を記述してsbtを使ってコンパイルし、さらにsbt-assemblyプラグインを用いてFat JARファイルを作成してJavaコマンドを使って実行してみると…
ファイルが見つからない旨のエラーが出ます。_| ̄|○
ファイルが見つからない旨のエラーだけではなく、使った覚えのないJNIのエラーまで発生していて、大変にイヤな感じです。
そこで、Fat JARファイルの中身を確認してみると…
スポンサーリンク
scala-libraryのJARファイルにありそうなファイルが一部Fat JARファイルの中にないように見えます。それも、パッケージ名が長めのクラスが属するディレクトリ階層が含まれていないように見えます。
一方で、同じプロジェクトを使ってLinux上でFat JARファイルを作成し、その中身を見てみると…
となっていて、Windows上で作成したFat JARファイルの中にはなかった scala.util.matchingパッケージのclassファイルが入っているように見えます。
Windows上で作成したFat JARファイルは、この状態でもWindows上のScalaのインタプリタ(scala-lang.orgのダウンロードページの「Other Resources」のところからダウンロードできるScalaのインストーラ等に入っています。)では実行できるので、とりあえずプログラム自体の動作確認を行うことにしました。
再挑戦。
「階層が深い」こと自体がFat JARファイル内に同梱されているファイル不足の原因だとすると、まず思い浮かぶのが「パスの長さ」です。
Windows 10では特別な設定を行わない限り、260文字までのパスしか使用できませんが、これに引っかかっている可能性がありそうです。
実はsbtはcompileタスクを実行すると、classファイルなどの中間ファイル的なものをプロジェクト内にディレクトリを作りつつ盛大に出力します。
そこで、Regexのclassファイルが出力されると思しきディレクトリのパスを調べてみると…
すごく、長いです(上図の赤矢印)。
ハッシュ値を16進表示したものがディレクトリ名として使われていて、その下にFat JARファイルの材料となると思われるファイルが書き出されています。ここにパス名が長いファイルが書き出されていないことが原因でそれらのファイル群がFat JARファイルに入らないのかもしれません。
というわけで、Windows 10 Homeにおけるプロジェクトファイルの置き場をドライブ直下、またはドライブ直下にできるだけ近いところに置いて、パスの文字列長をできるだけ短くすれば、この問題は発生しなくなるのかもしれません。とりあえず、JNIのエラーについてはいったん放置することにします。
そうは言っても、ホームディレクトリの下にはできる限り置きたいので、ホームディレクトリの直下にscalaというディレクトリを作成し、その下に置いてみます。
そしてsbtを使ってFat JARファイルを作成し、移動前と同様の方法でFat JARファイルの中身を確認すると…
RegexのclassファイルがFat JARファイルの中に入ったようです。👍
再度実行。
パスの文字列の長さを短くするとFat JARファイルの中に入るscalaのclassファイルが増えることがわかったところで、以下のようなファイルを準備して…
2019/09/04 新横浜→品川 1270円
これを標準入力から与えつつ、Fat JARファイルを実行してみます。
すると…
無事、実行することができました!! 🎉
JNIのエラーも表示されなくなりました!!!
まとめ
JNIのエラーが表示されてしまったこともあり、Javaのバージョンをいろいろ変えてみて実行してみたり、文字コードの設定の方法についてもいろいろ変えてみたりしましたが、根本的な原因は途中で出力されるディレクトリが長すぎてWindows 10 Homeのパスの長さ制限に引っかかってしまうのが原因のようです。なお、Javaについては以下のバージョンで動作を確認しています。
- RedHat OpenJDK 8
- Amazon Corretto 11
パスの長さ制限についての設定を変更または解除できるものなら解除する方が良いとは思います。しかし、Windows 10 Homeではレジストリの編集が必要ですので、今回はパスの長さを短くすることで解決を図りました。
Windows 10でScalaのそこそこ大きなコードを書いていて、特にJava関連の構成を大きく変えたわけでもないのに、原因不明の「ファイルが見つかりません」的なエラーが連発するような際はご参考にしていただけると幸いです。
この記事は以上です。