PlantUML-Server(docker)で別ファイルを!includeする

# はじめに

みなさんこんにちは、[PlantUML](https://plantuml.com/ja/)使ってますか？

おそらく多くの人は[公式のWEBサーバ](https://www.plantuml.com/plantuml/uml/SyfFKj2rKt3CoKnELR1Io4ZDoSa70000)、もしくは[VSCodeの拡張機能](https://marketplace.visualstudio.com/items?itemName=jebbs.plantuml)を使っているんじゃないかと思います。

[PlantUMLのWEBアプリ](https://github.com/plantuml/plantuml-server)(以下、PlantUML-Server)をローカルにホストすることもdocker経由で簡単にできるのですが、PlantUMLの外部ファイル(.iuml)を読み込む際には設定が必要だったのでメモを残しておきます。

<br>
PlantUML-ServerのREADMEに書いてあったと言うオチですが、同じシチュエーションになった人の助けになればと思います！

また、Javaやdocker等の操作は初心者なので、間違いがあれば訂正いただけると助かります！！

## 1. 確認環境

| ツール          | バージョン |
| --------------- | ---------- |
| PlantUML-Server | v1.2024.4  |

## 2. PlantUML-Serverを立てる

[クイックスタート](https://plantuml.com/ja/starting)を参考に進めていきます。

dockerのインストールは完了済みの想定です。

まずdocker imageをダウンロードしてきます。

```sh
docker pull plantuml/plantuml-server:jetty
```

次にPlantuml-Serverの動作を確認します。

```sh
docker run -d -p 8080:8080 plantuml/plantuml-server:jetty
```

:::note 
docker runで使うと便利なオプション
- `--rm` コンテナ停止時に削除します
- `--name NAME`: コンテナの名前をつけます
:::

起動に成功していれば、 http://localhost:8080 でアクセスできるはずです。

## 3. PlantUMLで外部ファイルを使用する

次にPlantUML (PlantUML-Serverではなく) を使って、複数のファイルで同じ処理を繰り返す場合に、共通部分を他のファイル(以下、外部ファイル: 拡張子は`.iuml`)に書き出して使用する方法を紹介します。

外部ファイルは、C言語の.hファイルのようなイメージです。

VSCodeを使うのが簡単なので、本項目ではVSCodeの拡張機能を使用していきます。

### 3.1. PlantUMLで描画（外部ファイルなし）

まず、PlantUMLでシーケンス図を描きます。
(拡張子はなんでも良いので、一般的な.pumlを使用します。)

```txt:test-1.puml
@startuml
skinparam ParticipantPadding 50
hide footbox

!procedure $echo($src, $dst, $str)
  $src ->  $dst : $str
  $dst --> $src : echo $str
!endprocedure

$echo(Bob, Alice, "hello")
|||
$echo(Bob, Alice, "world")
@enduml
```

```plantuml
@startuml
skinparam ParticipantPadding 50
hide footbox

!procedure $echo($src, $dst, $str)
  $src ->  $dst : $str
  $dst --> $src : echo $str
!endprocedure

$echo(Bob, Alice, "hello")
|||
$echo(Bob, Alice, "world")
@enduml
```

<details><summary>コードの説明</summary>

1. まずUMLを描画するおまじないと、基本的な図の表示設定を行っています。

    ```txt
    @startuml
    skinparam ParticipantPadding 50
    hide footbox
    ...
    @enduml
    ```
    
    - `@startuml`~`@enduml`: 囲った部分がUMLとしてレンダリングされます。
        PlantUML-Serverでは1つしか記述できませんので、最初はとりあえず書くという認識で良さそうです。
    - `skinparam ParticipantPadding 50`: 参加者間(BobとAlice)のパディングを設定しています。
    - `hide footbox`: 図下に表示されるフッターを非表示にしています。

2. 次に簡単なプロシージャを定義します。

    ```txt
    !procedure $echo($src, $dst, $str)
      $src ->  $dst : $str
      $dst --> $src : echo $str
    !endprocedure
    ```
        
    - `!procedure`~`!endprocedure`: 囲った部分がプロシージャとして定義されます。
        C言語の関数のように記載でき、引数をそれぞれ`$src, $dst, $str`に設定しています。
        詳しくは[こちら](https://plantuml.com/ja/preprocessing#01deeaa12f04fb9d)を参照してください。

3. 最後に先ほど定義したプロシージャを呼び出していきます。
    見やすいように`|||`にて間隔を空けています。

    ```txt
    $echo(Bob, Alice, "hello")
    |||
    $echo(Bob, Alice, "world")
    ```

詳細は[PlantUMLの公式サイト](https://plantuml.com/ja/sequence-diagram)を参照してください。

</details>
<br>

### 3.2. PlantUMLで描画（外部ファイルあり）

上記ファイル内の設定部分(`skinparam`や`hide footbox`)やプロシージャ(`!procedure $echo`~`!endprocedure`で囲まれた部分)を外部ファイルに書き出したいと思います。

各ファイルから、`!include ファイル名`を呼ぶことで使用することができます。
公式ドキュメントには[こちら](https://plantuml.com/ja/preprocessing#393335a6fd28a804)に記載があります。

<br>
今回は以下の2つのファイルを同じディレクトリに入れます。

1. `include.iuml`: プロシージャを定義しているファイル。
1. `test-2.puml`: プロシージャを呼び出すファイル。
    同じような内容を`test-*.puml`に書いていく想定。

<details><summary>include.iuml</summary><div>

```txt:include.iuml
@startuml
skinparam ParticipantPadding 50
hide footbox

!procedure $echo($src, $dst, $str)
  $src ->  $dst : $str
  $dst --> $src : echo $str
!endprocedure
@enduml
```

</div></details>

<details><summary>test-2.puml</summary><div>

```txt:test-2.puml
@startuml
!include include.iuml

$echo(Dave, Charlie, "Good")
|||
$echo(Dave, Charlie, "Bye")
@enduml
```

</div></details>

VSCodeの拡張機能なら特に設定せずとも表示されるはずです。

![plantuml.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519001/8991b4a5-c07c-6fd0-78aa-3463a62c4e07.png)

### 3.3. CLIからjarファイルを実行する場合

余談ですが、PlantUMLを`.jar`ファイルでダウンロードした場合、
コマンドラインからファイルを指定することで画像を出力させます。

この場合、`-Dplantuml.path.include=path/to/file`のオプションを設定することでローカルのファイルを読み込めます。公式ドキュメントでは[こちら](https://plantuml.com/ja/preprocessing#4d03410776ebc180)です。

```sh
java -Dplantuml.path.include=path/to/include.iulm -jar platnuml.jar test-2.puml
```

## 4. PlantUML-Server(docker)で別ファイルをIncludeする

dockerでPlantUML-Serverを起動した場合はjavaのコマンドを叩いている訳ではないので、どうやってオプションを設定するか悩みました。

**解決方法：**
結局は、[公式README](https://github.com/plantuml/plantuml-server?tab=readme-ov-file#how-to-set-plantuml-options)のオプションを設定する方法で解決しました。

まず、環境変数`PLANTUML_PROPERTY_FILE`に使用する設定ファイルを用意します。

<details><summary>java.properties</summary><div>

```txt:java.properties
plantuml.include.path=/include
```

:::note
指定するパス(上記例では`/include`)は、ローカルのパスではなく、docker上のマウント先ディレクトリを設定します。
:::

</div></details>

そして設定ファイルと.iumlファイルをディレクトリに格納します。

```console
$tree
.
├── include.iuml
└── java.properties
```

:::note 
上記ファイルを同一ディレクトリに格納する必要はないです。
また、マウントではなくコピーを行っても良いですが、後から編集する場合を考えてマウントにしています。
:::

dockerを実行する際に、設定ファイルと外部フォルダがあるフォルダをマウントし、環境変数に設定ファイルのパスを設定します。

```sh
docker run -d --mount type=bind,source=.,target=/include -e PLANTUML_PROPERTY_FILE=/include/java.properties -p 8080:8080 plantuml/plantuml-server:jetty
```

- `--mount type=bind,source=.,target=/include`: ローカルのディレクトリをコンテナにマウントします。`-v`でも同様の操作が行えます。
- `-e PLANTUML_PROPERTY_FILE=/include/java.properties`: 環境変数に値を設定します。

以上で`test-2.puml`の内容を書けば、無事`include.iuml`の内容を読み込んでくれるはずです！

![test-2.puml](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519001/abb41448-a05c-ecd3-dbbf-985d4140030d.png)

## 最後に

dockerにディレクトリをマウントしているので、ローカルの外部ファイルを変更すればPlantUML-Serverの内容も変更されます。ただし描画は画面左のエディタ内の内容が変更された時にされて、もっと言うとその内容がURLになっています。

キャッシュにより変更が反映されないので、その場合はスーパーリロードが必要です。