ARM版JDKでPlayFrameworkを実行する方法

# 本記事の環境

- 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](https://www.azul.com/downloads/zulu-community/) を使用。

# 発生する問題

PlayFrameworkの開発環境起動コマンド（```sbt run```) を実行すると、以下のようなエラーが表示されてしまいエラーで終了する。（X86版のJDKでは動作する）

##### エラー内容

```java
[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` に以下を追加する。

```scala
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](https://github.com/playframework/play-file-watch) では [directory-watcher](https://github.com/gmethvin/directory-watcher) ライブラリを使用してソースコード変更検知監視を行っています。
OSがMacOSの場合は [DefaultFileWatchService](https://github.com/playframework/play-file-watch/blob/d3ea0ada4876742297051b31b282ab14cfa11856/src/main/scala/play/dev/filewatch/DefaultFileWatchService.scala#L33
)クラスでMac用の処理を呼び出していますが、[MacOSXListeningWatchService](https://github.com/gmethvin/directory-watcher/blob/8469ca447ee5f16cb33fd25eb4f4e701e217d397/core/src/main/java/io/methvin/watchservice/MacOSXListeningWatchService.java#L38) クラスでは旧世代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