本記事の環境
- OS: Mac OS Big Sur 11.1
- JDK: Zulu11.45+27-CA (build 11.0.10+9-LTS) 64bit ARM
- sbt: 1.4.7
- Scala: 2.13.4
- PlayFramework: 2.8.7
従来のX86用JDKのRosetta 2による変換実行による使用ではなく、Apple Silicon搭載Mac(ARM)用にコンパイルされた Zulu JDK を使用。
発生する問題
PlayFrameworkの開発環境起動コマンド(sbt run
) を実行すると、以下のようなエラーが表示されてしまいエラーで終了する。(X86版のJDKでは動作する)
エラー内容
[error] java.lang.UnsatisfiedLinkError: /Users/username/Library/Caches/JNA/temp/jna10769098174142672166.tmp: dlopen(/Users/username/Library/Caches/JNA/temp/jna10769098174142672166.tmp, 1): no suitable image found. Did find:
[error] /Users/username/Library/Caches/JNA/temp/jna10769098174142672166.tmp: no matching architecture in universal wrapper
[error] /Users/username/Library/Caches/JNA/temp/jna10769098174142672166.tmp: no matching architecture in universal wrapper
[error] at java.base/java.lang.ClassLoader$NativeLibrary.load0(Native Method)
[error] at java.base/java.lang.ClassLoader$NativeLibrary.load(ClassLoader.java:2442)
[error] at java.base/java.lang.ClassLoader$NativeLibrary.loadLibrary(ClassLoader.java:2498)
[error] at java.base/java.lang.ClassLoader.loadLibrary0(ClassLoader.java:2694)
[error] at java.base/java.lang.ClassLoader.loadLibrary(ClassLoader.java:2627)
[error] at java.base/java.lang.Runtime.load0(Runtime.java:768)
[error] at java.base/java.lang.System.load(System.java:1837)
[error] at com.sun.jna.Native.loadNativeDispatchLibraryFromClasspath(Native.java:1018)
[error] at com.sun.jna.Native.loadNativeDispatchLibrary(Native.java:988)
[error] at com.sun.jna.Native.<clinit>(Native.java:195)
[error] at io.methvin.watchservice.jna.CarbonAPI.<clinit>(CarbonAPI.java:20)
[error] at io.methvin.watchservice.jna.CFStringRef.toCFString(CFStringRef.java:23)
[error] at io.methvin.watchservice.MacOSXListeningWatchService.register(MacOSXListeningWatchService.java:127)
[error] at io.methvin.watchservice.WatchablePath.register(WatchablePath.java:50)
[error] at io.methvin.watcher.DirectoryWatcher.register(DirectoryWatcher.java:341)
[error] at io.methvin.watcher.DirectoryWatcher.registerAll(DirectoryWatcher.java:315)
[error] at io.methvin.watcher.DirectoryWatcher.<init>(DirectoryWatcher.java:176)
[error] at io.methvin.watcher.DirectoryWatcher$Builder.build(DirectoryWatcher.java:117)
[error] at play.dev.filewatch.DefaultFileWatchService.watch(DefaultFileWatchService.scala:41)
[error] at play.dev.filewatch.FileWatchService$$anon$1.watch(FileWatchService.scala:90)
[error] at play.runsupport.Reloader.<init>(Reloader.scala:443)
[error] at play.runsupport.Reloader$.reloader$lzycompute$1(Reloader.scala:283)
[error] at play.runsupport.Reloader$.play$runsupport$Reloader$$reloader$1(Reloader.scala:275)
[error] at play.runsupport.Reloader$.startDevMode(Reloader.scala:306)
[error] at play.sbt.run.PlayRun$.devModeServer$lzycompute$1(PlayRun.scala:98)
[error] at play.sbt.run.PlayRun$.devModeServer$1(PlayRun.scala:81)
[error] at play.sbt.run.PlayRun$.$anonfun$playRunTask$3(PlayRun.scala:105)
[error] at play.sbt.run.PlayRun$.$anonfun$playRunTask$3$adapted(PlayRun.scala:67)
[error] at scala.Function1.$anonfun$compose$1(Function1.scala:49)
現時点での解決方法
build.sbt
に以下を追加する。
PlayKeys.fileWatchService := play.dev.filewatch.FileWatchService.jdk7(play.sbt.run.toLoggerProxy(sLog.value))
// 以下のような形式でも良い
// PlayKeys.fileWatchService := play.dev.filewatch.FileWatchService.polling(2000)
原因
PlayFrameworkの内部ライブラリ play-file-watch では directory-watcher ライブラリを使用してソースコード変更検知監視を行っています。
OSがMacOSの場合は DefaultFileWatchServiceクラスでMac用の処理を呼び出していますが、MacOSXListeningWatchService クラスでは旧世代Mac用ライブラリ実行用のCarbon APIを呼び出す実装となっており、ARMでコンパイルされたZulu JDKではCarbonAPI用のライブラリが(おそらく)未搭載のため実行時にファイルが見つからず失敗しているためと思われます。
また、Zulu JDKのIO実装がWindows/Linuxの実装に合わせて実装できているため、解決方法に記載したようなWindonws/Linux用のfileWatchServiceの指定方法でも動作できているのではないかと推測しています。
その他
PlayFrameworkでは play-file-watch ライブラリを今後置き換えるかどうかの議論を以下で行っており、その際にこの問題が同時に修正されるのではないかと思われます。
https://github.com/playframework/playframework/issues/9956