# 通知設計書 5-NullIdDescriptor警告（NullIdDescriptorMonitor）

## 概要

本ドキュメントは、DescriptorのIDがnullの場合に管理者へ警告を表示するNullIdDescriptorMonitor（NullIdDescriptor警告）の通知設計について記述する。

### 本通知の処理概要

NullIdDescriptorMonitorは、Jenkins起動時にすべてのDescriptor拡張ポイントを検査し、getId()メソッドがnullを返すか例外をスローするDescriptorを検出して、管理者に対して警告メッセージを表示する通知機能である。

**業務上の目的・背景**：Descriptorクラスは、Jenkinsのプラグインや設定項目を表す重要なコンポーネントである。各DescriptorはユニークなIDを持つ必要があり、このIDは設定の永続化やUI生成に使用される。IDがnullの場合、JENKINS-8866のようなエラーが発生し、プラグインの動作に支障をきたす。本通知は、問題のあるDescriptorを早期に検出し、プラグイン開発者への報告やプラグインの更新を促すことを目的とする。

**通知の送信タイミング**：Jenkins起動時のEXTENSIONS_AUGMENTED初期化フェーズ後に、`@Initializer(after = EXTENSIONS_AUGMENTED)`で`verify()`メソッドが呼び出される。このメソッドで全Descriptorを検査し、問題があるものをproblemsリストに追加する。

**通知の受信者**：Jenkins管理画面（/manage）にアクセスできる管理者が対象となる。

**通知内容の概要**：「以下のDescriptorでIDがnullまたは例外が発生」という旨のメッセージとともに、問題のあるDescriptorの一覧（クラス名、表示名、プラグイン名）が表示される。

**期待されるアクション**：管理者は問題のあるプラグインを特定し、プラグインの更新、プラグイン開発者への報告、または必要に応じてプラグインの無効化を検討することが期待される。

## 通知種別

アプリ内通知（管理画面バナー表示）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（画面レンダリング時） |
| 優先度 | 高（システム起動時検証） |
| リトライ | 無（画面表示のため不要） |

### 送信先決定ロジック

Jenkins管理画面（/manage）にアクセスした管理者に対して表示される。problemsリストに1件以上のエントリがある場合に有効化。

## 通知テンプレート

### アプリ内通知の場合

| 項目 | 内容 |
|-----|------|
| 表示位置 | 管理画面上部 |
| アラートタイプ | jenkins-alert-danger（危険） |
| 形式 | HTML |

### 本文テンプレート

```html
<div class="jenkins-alert jenkins-alert-danger">
  <dl>
    <dt>${%blurb}</dt>
    <j:forEach var="d" items="${it.problems}">
      <dd>${%problem(d, d.displayName, app.pluginManager.whichPlugin(d.getClass()))}</dd>
    </j:forEach>
  </dl>
</div>
```

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| it.problems | 問題のあるDescriptorリスト | NullIdDescriptorMonitor.problems | Yes |
| d | 各Descriptor | 反復処理 | Yes |
| d.displayName | Descriptorの表示名 | Descriptor.getDisplayName() | Yes |
| app.pluginManager.whichPlugin(d.getClass()) | Descriptorを提供するプラグイン | PluginManager.whichPlugin() | No |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| システムイベント | Jenkins起動（EXTENSIONS_AUGMENTED後） | Descriptor.getId()がnullまたは例外 | verify()で検出 |
| 画面操作 | 管理画面アクセス | !problems.isEmpty() | problemsリストが空でない場合 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| problems.isEmpty() | 問題のあるDescriptorが存在しない場合 |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[Jenkins起動] --> B[EXTENSIONS_AUGMENTED完了]
    B --> C[verify実行]
    C --> D[全Descriptorを取得]
    D --> E{各Descriptorを検査}
    E --> F{getId呼び出し}
    F -->|例外発生| G[ログ出力 + problemsに追加]
    F -->|null返却| H[ログ出力 + problemsに追加]
    F -->|正常| I[次のDescriptorへ]
    G --> I
    H --> I
    I --> E
    E -->|全件完了| J[管理画面アクセス]
    J --> K{isActivated?}
    K -->|No| L[表示しない]
    K -->|Yes| M[警告バナー表示]
    M --> N[問題Descriptor一覧表示]
    L --> O[終了]
    N --> O
```

## データベース参照・更新仕様

### 参照テーブル一覧

該当なし（メモリ上のExtensionListを参照）

### 参照データ構造

| データ構造 | 用途 | 備考 |
|-----------|------|------|
| ExtensionList<Descriptor> | 全Descriptorの取得 | Jenkins.getExtensionList(Descriptor.class) |
| List<Descriptor> problems | 問題のあるDescriptorの保持 | ArrayList |

### 更新ファイル

該当なし（この通知にはDismiss機能がない）

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| getId例外 | Descriptor.getId()が例外をスロー | ログ出力（SEVERE）、problemsに追加 |
| getId null | Descriptor.getId()がnullを返す | ログ出力（SEVERE）、problemsに追加 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 0（起動時の1回のみ検査） |
| リトライ間隔 | - |
| リトライ対象エラー | - |

## 配信設定

### レート制限

該当なし（画面表示のため）

### 配信時間帯

制限なし（常時有効）

## セキュリティ考慮事項

- 管理者権限が必要
- Dismiss機能なし（問題が解決されるまで継続的に表示）

## 備考

- Symbol: `nullId`
- パッケージ: hudson.diagnosis
- 導入バージョン: Jenkins 1.402以降（@since 1.402）
- 関連Issue: JENKINS-8866
- メッセージリソース: hudson/diagnosis/Messages.properties

---

## コードリーディングガイド

本通知を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

Descriptorクラスとその検証方法を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Descriptor.java | `core/src/main/java/hudson/model/Descriptor.java` | getId()メソッドの仕様 |
| 1-2 | NullIdDescriptorMonitor.java | `core/src/main/java/hudson/diagnosis/NullIdDescriptorMonitor.java` | problemsリストの構造 |

**読解のコツ**: Descriptor.getId()は通常クラス名を返すが、オーバーライドにより問題が発生する可能性がある点に注目。

#### Step 2: 検証処理を理解する

verify()メソッドがエントリーポイント。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | NullIdDescriptorMonitor.java | `core/src/main/java/hudson/diagnosis/NullIdDescriptorMonitor.java` | verify()での全Descriptor検査 |

**主要処理フロー**:
1. **69行目**: `@Initializer(after = EXTENSIONS_AUGMENTED)` - 初期化タイミング指定
2. **70-71行目**: `verify()` - Jenkins.get()で全Descriptorを取得
3. **72-88行目**: 各Descriptorを検査
   - **73行目**: whichPlugin()でプラグイン特定
   - **75-76行目**: getId()呼び出し（try-catch）
   - **77-81行目**: 例外発生時の処理（SEVERE ログ + problems追加）
   - **83-87行目**: null返却時の処理（SEVERE ログ + problems追加）

#### Step 3: 表示処理を理解する

isActivated()とビューテンプレート。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | NullIdDescriptorMonitor.java | `core/src/main/java/hudson/diagnosis/NullIdDescriptorMonitor.java` | isActivated()、getProblems() |
| 3-2 | message.jelly | `core/src/main/resources/hudson/diagnosis/NullIdDescriptorMonitor/message.jelly` | 警告UIの構造 |

**主要処理フロー**:
- **61-63行目**: `isActivated()` - problemsが空でないかチェック
- **65-67行目**: `getProblems()` - 不変リストとして返却

### プログラム呼び出し階層図

```
[起動時]
Jenkins.start()
    │
    └─ InitMilestone.EXTENSIONS_AUGMENTED
           │
           └─ NullIdDescriptorMonitor.verify()
                  │
                  ├─ Jenkins.getExtensionList(Descriptor.class)
                  │
                  └─ 各Descriptor
                         ├─ PluginManager.whichPlugin(d.getClass())
                         │
                         └─ d.getId()
                                │
                                ├─ 例外 → problems.add(d)
                                ├─ null → problems.add(d)
                                └─ 正常 → continue

[画面表示]
Jenkins.getAdministrativeMonitors()
    │
    ├─ NullIdDescriptorMonitor.isActivated()
    │      └─ return !problems.isEmpty()
    │
    └─ message.jelly
           └─ it.getProblems() → 一覧表示
```

### データフロー図

```
[入力]                    [処理]                         [出力]

Jenkins起動 ───▶ EXTENSIONS_AUGMENTED ───▶ verify()実行
                         │
                         └─ ExtensionList<Descriptor>
                                    │
                                    ▼
                            各Descriptor.getId()
                                    │
                         ┌──────────┼──────────┐
                         ▼          ▼          ▼
                       例外       null       正常
                         │          │          │
                         └────┬─────┘          │
                              ▼                │
                       problems追加            │
                              │                │
                              └────────────────┘
                                      │
                                      ▼
                              管理画面アクセス時
                                      │
                                      ▼
                              警告バナー表示
                              (問題Descriptor一覧)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| NullIdDescriptorMonitor.java | `core/src/main/java/hudson/diagnosis/NullIdDescriptorMonitor.java` | ソース | メインクラス |
| AdministrativeMonitor.java | `core/src/main/java/hudson/model/AdministrativeMonitor.java` | ソース | 基底クラス |
| Descriptor.java | `core/src/main/java/hudson/model/Descriptor.java` | ソース | 検査対象クラス |
| PluginManager.java | `core/src/main/java/hudson/PluginManager.java` | ソース | プラグイン特定 |
| message.jelly | `core/src/main/resources/hudson/diagnosis/NullIdDescriptorMonitor/message.jelly` | テンプレート | 警告バナーUI |
| description.jelly | `core/src/main/resources/hudson/diagnosis/NullIdDescriptorMonitor/description.jelly` | テンプレート | 説明文 |
| Messages.properties | `core/src/main/resources/hudson/diagnosis/Messages.properties` | リソース | メッセージ定義 |
