SDKMANのJDKをSystem JDKとしてmacOSに誤認させる

# なぜ誤認させたい？

- SDKMANをセットアップすると`JAVA_HOME`をSDKMANがよしなにしてくれるようなる
- `/usr/bin/java`, `/usr/libexec/java_home`などが変わらずSystem JDKの場所である`/Library/Java/JavaVirtualMachines`配下のJDKを参照する
- XcodeのScriptPhaseなど**JAVA_HOMEを見ず、強制的にSystem JDKを参照する**ライブラリやプラグイン類がいる
- どうにかしてPCにinstallするJDKをSDKMANだけに寄せたいのでSDKMANでinstallしたJDKをSystem JDKに誤認させたい

# SDKMAN以外のJDKはどうなっているか

- システムのJDKは`/Library/Java/JavaVirtualMachines`にinstallもしくはsymbolic linkを貼ったものであるパターンが多い
  - [jdk.java.net](https://jdk.java.net/)からinstallする場合、tarファイルを`/Library/Java/JavaVirtualMachines`に展開する
  - Homebrewで[openjdk](https://formulae.brew.sh/formula/openjdk)をinstallする場合、システムが認識できるようにシンボリックリンクを貼るように表示される
  - ```shell
    For the system Java wrappers to find this JDK, symlink it with
    sudo ln -sfn /opt/homebrew/opt/openjdk/libexec/openjdk.jdk /Library/Java/JavaVirtualMachines/openjdk.jdk

    openjdk is keg-only, which means it was not symlinked into /opt/homebrew,
    because macOS provides similar software and installing this software in
    parallel can cause all kinds of trouble.

    If you need to have openjdk first in your PATH, run:
    echo 'export PATH="/opt/homebrew/opt/openjdk/bin:$PATH"' >> $HOME.zshrc

    For compilers to find openjdk you may need to set:
    export CPPFLAGS="-I/opt/homebrew/opt/openjdk/include"
    ```
- `/Library/Java/JavaVirtualMachines`にApplication Bundle形式でJDKが展開される
  - Distributionによって若干の差異はあるが以下のようなファイル構造になる
    - ```md
        /Library/Java/JavaVirtualMachines
        └── Contents
            ├── CodeResources
            ├── Home
            │   ├── bin
            │   ├── conf
            │   ├── include
            │   ├── jmods
            │   ├── legal
            │   ├── lib
            │   └── release
            ├── Info.plist
            ├── MacOS
            │   └── libjli.dylib
            └── _CodeSignature
                └── CodeResources
        ```

# SDKMANでinstallしたJDKはどうなっているか

- 特別なinstall設定を行わない場合`$HOME/.sdkman/candidates/java`配下にinstallされる
- Distributionによって若干の差異はあるが、以下のようなファイル構造になる
- 概ねApplication Bundle形式のHomeディレクトリの内容だと考えていい

```md
$HOME/.sdkman/candidates/java
└──17.0.13-tem
    ├── NOTICE
    ├── bin
    ├── conf
    ├── include
    ├── jmods
    ├── legal
    ├── lib
    ├── man
    └── release
```

# System JDKとして認識されるためにはどうすればよいか

- OpenJDKを参考にすると`/Library/Java/JavaVirtualMachines/Contents`配下にsymbolic linkとファイルを配置する必要がある
  - bin, conf各種がある`Home`
  - `libjli.dylib`がある`MacOS`
  - `Info.plist`の作成

## `Home`,`MacOS`ディレクトリへのsymbolic link作成

- SDKMANでinstallしたJDKのsymbolic linkを作成してあげればよい

```shell
# directoryの作成(sdkman.jdkは任意の名前)
sudo mkdir -p /Library/Java/JavaVirtualMachines/sdkman.jdk/Contents/Home
sudo mkdir -p /Library/Java/JavaVirtualMachines/sdkman.jdk/Contents/MacOS

# bin, conf, include, jmods, legal, lib系のsymbolic link作成
sudo ln -sfn ~/.sdkman/candidates/java/17.0.13-tem/* /Library/Java/JavaVirtualMachines/sdkman.jdk/Contents/Home

# libjli.dylibの位置ははDistributionによって異なるので注意する必要がある(↓はtemurinの場合)
sudo ln -sfn ~/.sdkman/candidates/java/17.0.13-tem/lib/libjli.dylib /Library/Java/JavaVirtualMachines/sdkman.jdk/Contents/MacOS/libjli.dylib
```

## `Info.plist`の作成

- Homebrewでopenjdkをinstallした際は`/opt/homebrew/Cellar/openjdk/23/libexec/openjdk.jdk/Contents`に以下のInfo.plistが配置されている

```xml:Info.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>CFBundleDevelopmentRegion</key>
    <string>English</string>
    <key>CFBundleExecutable</key>
    <string>libjli.dylib</string>
    <key>CFBundleGetInfoString</key>
    <string>OpenJDK 23</string>
    <key>CFBundleIdentifier</key>
    <string>net.java.openjdk.jdk</string>
    <key>CFBundleInfoDictionaryVersion</key>
    <string>7.0</string>
    <key>CFBundleName</key>
    <string>OpenJDK 23</string>
    <key>CFBundlePackageType</key>
    <string>BNDL</string>
    <key>CFBundleShortVersionString</key>
    <string>23</string>
    <key>CFBundleSignature</key>
    <string>????</string>
    <key>CFBundleVersion</key>
    <string>0</string>
    <key>NSMicrophoneUsageDescription</key>
    <string>The application is requesting access to the microphone.</string>
    <key>JavaVM</key>
    <dict>
        <key>JVMCapabilities</key>
        <array>
                <string>CommandLine</string>
        </array>
        <key>JVMMinimumFrameworkVersion</key>
        <string>13.2.9</string>
        <key>JVMMinimumSystemVersion</key>
        <string>11.00.00</string>
        <key>JVMPlatformVersion</key>
        <string>23</string>
        <key>JVMVendor</key>
        <string>Homebrew</string>
        <key>JVMVersion</key>
        <string>23</string>
    </dict>
</dict>
</plist>

```

- Systemが最低限認識できるようにすると以下のようなInfo.plistになる

```xml:Info.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>CFBundleIdentifier</key>
    <string>sdkman.current.jdk</string>
    <key>CFBundleName</key>
    <string>SDKMAN current JDK</string>
    <key>CFBundleExecutable</key>
    <string>libjli.dylib</string>
    <key>JavaVM</key>
    <dict>
        <key>JVMCapabilities</key>
        <array>
            <string>CommandLine</string>
        </array>
        <key>JVMPlatformVersion</key>
        <string>${sdk current java}</string>
        <key>JVMVendor</key>
        <string>SDKMAN</string>
        <key>JVMVersion</key>
        <string>${sdk current java}</string>
    </dict>
</dict>
</plist>
```

:::note info
要素さえ揃っていれば、`CFBundleIdentifier`, `CFBundleName`, `JavaVM`は任意の値で問題ない
:::


# 結果

- ここまで実施すると`/usr/bin/java`, `/usr/libexec/java_home`がSDKMANでinstallしたJDKをSystem JDKとして認譋するようになる

```shell-session
$ where java
/usr/bin/java
$HOME/.sdkman/candidates/java/current/bin/java 

$ /usr/bin/java -version
openjdk version "17.0.13" 2024-10-15
OpenJDK Runtime Environment Temurin-17.0.13+11 (build 17.0.13+11)
OpenJDK 64-Bit Server VM Temurin-17.0.13+11 (build 17.0.13+11, mixed mode, sharing)

$ /usr/libexec/java_home -V
Matching Java Virtual Machines (1):
    ${sdk current java} (arm64) "SDKMAN" - "SDKMAN current JDK" /Library/Java/JavaVirtualMachines/sdkman.jdk/Contents/Home
/Library/Java/JavaVirtualMachines/sdkman.jdk/Contents/Home
```

### Ref

https://stackoverflow.com/questions/76523553/how-to-make-usr-libexec-java-home-find-jdk-installed-using-sdkman-on-macos