# 機能設計書 51-拡張機構

## 概要

本ドキュメントは、Jenkinsの拡張機構（Extension Point Framework）の機能設計を記述する。この機構はJenkinsのプラグインシステムの根幹を成し、プラグインによる機能拡張を可能にする基盤である。

### 本機能の処理概要

**業務上の目的・背景**：Jenkinsは単なるCI/CDツールではなく、プラグインによって無限に機能拡張可能なプラットフォームである。拡張機構は、コア機能とプラグイン間の疎結合を実現し、サードパーティ開発者がJenkinsの機能を自由に拡張できる仕組みを提供する。これにより、組織固有のビルド要件やツール連携に対応でき、Jenkinsエコシステムの発展を支えている。

**機能の利用シーン**：
- プラグイン開発者が新しいビルドステップ、SCM連携、認証方式などを追加する際
- Jenkins起動時にすべての拡張ポイント実装を自動発見・登録する際
- 動的なプラグインのロード・アンロード時に拡張を更新する際
- 特定の拡張ポイントの実装一覧を取得してUI選択肢を構築する際

**主要な処理内容**：
1. `@Extension`アノテーションによる拡張実装のマーキング
2. SezPozライブラリを使用したコンパイル時のインデックス生成
3. `ExtensionFinder`による実行時の拡張実装発見
4. `ExtensionList`への拡張インスタンスの登録・管理
5. 拡張の優先順位（ordinal）に基づくソート
6. 動的プラグインロード時の拡張リフレッシュ

**関連システム・外部連携**：
- SezPoz: アノテーション処理とインデックス生成
- Google Guice: 依存性注入（オプション）
- プラグインマネージャー: プラグインのライフサイクル管理

**権限による制御**：拡張機構自体は権限制御を行わない。各拡張ポイントの実装が個別に権限チェックを実装する。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 38 | インストール済みプラグイン | 補助機能 | プラグイン拡張情報の表示 |

## 機能種別

フレームワーク基盤 / メタデータ管理 / オブジェクト生成・管理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| extensionType | Class<T> | Yes | 取得したい拡張ポイントの型 | null不可、ExtensionPointまたはDescribableのサブタイプ |

### 入力データソース

- アノテーション: `@Extension`が付与されたクラス、メソッド、フィールド
- SezPozインデックス: `META-INF/annotations/`配下に生成されるインデックスファイル
- プラグインのクラスローダー: 各プラグインJARからのクラス読み込み

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| extensions | List<T> | 指定された型の拡張インスタンスリスト（ordinal降順） |
| extensionComponents | List<ExtensionComponent<T>> | 拡張インスタンスとメタデータのラッパー |

### 出力先

- メモリ: `ExtensionList`内のキャッシュされた拡張リスト
- Jenkins.extensionLists: 型ごとのExtensionListマップ

## 処理フロー

### 処理シーケンス

```
1. 拡張ポイント型の指定
   └─ ExtensionList.lookup(Class<T>)を呼び出し
2. ExtensionListの取得または作成
   └─ Jenkins.getExtensionList()でキャッシュを確認
3. 遅延ロード判定
   └─ 未ロードの場合のみload()を実行
4. ExtensionFinderによる発見
   └─ 全ExtensionFinderが順次拡張を探索
5. インスタンス生成
   └─ クラス、ファクトリメソッド、または静的フィールドから取得
6. ソートと登録
   └─ ordinal値でソートしてリストに格納
7. リスナー通知
   └─ ExtensionListListenerへ変更を通知
```

### フローチャート

```mermaid
flowchart TD
    A[ExtensionList.lookup呼び出し] --> B{Jenkins存在?}
    B -->|No| C[空のExtensionListを返却]
    B -->|Yes| D[Jenkins.getExtensionList]
    D --> E{キャッシュ有?}
    E -->|Yes| F[キャッシュを返却]
    E -->|No| G[新規ExtensionList作成]
    G --> H[ensureLoaded実行]
    H --> I{プラグイン準備完了?}
    I -->|No| J[legacyInstancesを返却]
    I -->|Yes| K[ExtensionFinder.find実行]
    K --> L[インスタンス生成]
    L --> M[ordinalでソート]
    M --> N[extensionsに格納]
    N --> O[ExtensionListを返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-51-01 | ordinalソート | 拡張はordinal値の降順でソートされる | 拡張リスト取得時 |
| BR-51-02 | シングルトン保証 | 同一拡張クラスは1インスタンスのみ | プラグインロード時 |
| BR-51-03 | 動的ロード対応 | dynamicLoadable=YESの場合のみ即時有効化 | プラグイン動的ロード時 |
| BR-51-04 | オプショナル拡張 | optional=trueの場合、ロードエラーを無視 | 依存プラグイン未インストール時 |

### 計算ロジック

ordinalによる優先順位計算:
- ordinal値が大きいほど優先度が高い（リストの先頭に配置）
- デフォルト値は0
- 同一ordinalの場合、登録順は不定

## データベース操作仕様

### 操作別データベース影響一覧

本機能はデータベースを直接操作しない。拡張の永続化はXMLファイルで行われる。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | InstantiationException | 拡張クラスのインスタンス化失敗 | ログ出力し、当該拡張をスキップ |
| - | LinkageError | クラスロード時の依存解決失敗 | optional=trueなら無視、それ以外はログ出力 |
| - | IllegalStateException | lookupSingletonで複数インスタンス | 例外をスロー |

### リトライ仕様

拡張のロードはリトライしない。失敗した拡張は当該起動サイクルでは使用不可となる。

## トランザクション仕様

本機能はトランザクション管理を行わない。拡張リストの更新はCopyOnWriteパターンで同期される。

## パフォーマンス要件

- 拡張リストの取得: O(1)（キャッシュヒット時）
- 初回ロード: プラグイン数とクラス数に比例
- メモリ使用量: 拡張インスタンス数に比例

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

- 拡張クラスはプラグインのセキュリティコンテキストで実行される
- 信頼されていないプラグインからの拡張は、適切な権限チェックなしにシステムリソースにアクセス可能
- プラグインマネージャーによるプラグインの署名検証が推奨される

## 備考

- ExtensionPointインターフェースはマーカーインターフェースであり、メソッド定義を持たない
- Descriptorは歴史的経緯によりExtensionPointを継承していないが、同様の機構で管理される

---

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

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

### 推奨読解順序

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

まず、拡張機構の核となるアノテーションとインターフェースを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Extension.java | `core/src/main/java/hudson/Extension.java` | @Extensionアノテーションの定義。ordinal、optional、dynamicLoadableの各属性を確認 |
| 1-2 | ExtensionPoint.java | `core/src/main/java/hudson/ExtensionPoint.java` | マーカーインターフェースの定義。拡張可能なコンポーネントの基底 |
| 1-3 | ExtensionComponent.java | `core/src/main/java/hudson/ExtensionComponent.java` | 拡張インスタンスとメタデータのラッパー |

**読解のコツ**: `@Extension`の`@Indexable`アノテーションに注目。これがSezPozによるコンパイル時インデックス生成を有効にする。

#### Step 2: エントリーポイントを理解する

拡張リストの取得が起点となる処理フローを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | ExtensionList.java | `core/src/main/java/hudson/ExtensionList.java` | 拡張リストの管理クラス。lookup()メソッドがエントリーポイント |

**主要処理フロー**:
1. **436-438行目**: `lookup()`メソッドでJenkinsインスタンスを確認し、ExtensionListを取得
2. **300-314行目**: `ensureLoaded()`で遅延ロードを実行
3. **383-390行目**: `load()`でPluginManager経由で拡張を発見

#### Step 3: 拡張発見メカニズムを理解する

ExtensionFinderによる拡張の発見プロセスを追う。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | ExtensionFinder.java | `core/src/main/java/hudson/ExtensionFinder.java` | 拡張発見の抽象基底クラス |

**主要処理フロー**:
- **87-151行目**: `ExtensionFinder`抽象クラスと`find()`メソッドの定義
- **190-200行目**: `DefaultGuiceExtensionAnnotation`によるGuice統合

#### Step 4: リスト操作を理解する

ExtensionListの追加・削除・ソート処理を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | ExtensionList.java | `core/src/main/java/hudson/ExtensionList.java` | add/remove/sortメソッドの実装 |

**主要処理フロー**:
- **260-281行目**: `add()`メソッドによる拡張の手動追加
- **407-411行目**: `sort()`メソッドによるordinalソート
- **329-360行目**: `refresh()`メソッドによる動的更新

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

```
ExtensionList.lookup(Class)
    │
    ├─ Jenkins.getExtensionList(Class)
    │      │
    │      └─ ExtensionList.ensureLoaded()
    │             │
    │             └─ ExtensionList.load()
    │                    │
    │                    └─ PluginManager.getPluginStrategy().findComponents()
    │                           │
    │                           └─ ExtensionFinder.find(Class, Hudson)
    │                                  │
    │                                  └─ SezPoz Index.load()
    │
    └─ ExtensionList.sort()
```

### データフロー図

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

@Extension              ExtensionFinder                ExtensionList
アノテーション  ───▶    (SezPozインデックス参照) ───▶    (ソート済み)
    │                        │                              │
    ▼                        ▼                              ▼
SezPozインデックス     インスタンス生成              キャッシュ保存
(META-INF/annotations)  (リフレクション)             (CopyOnWrite)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| Extension.java | `core/src/main/java/hudson/Extension.java` | ソース | 拡張マーキングアノテーション |
| ExtensionPoint.java | `core/src/main/java/hudson/ExtensionPoint.java` | ソース | 拡張ポイントマーカーインターフェース |
| ExtensionList.java | `core/src/main/java/hudson/ExtensionList.java` | ソース | 拡張リスト管理 |
| ExtensionFinder.java | `core/src/main/java/hudson/ExtensionFinder.java` | ソース | 拡張発見メカニズム |
| ExtensionComponent.java | `core/src/main/java/hudson/ExtensionComponent.java` | ソース | 拡張インスタンスラッパー |
| ExtensionListListener.java | `core/src/main/java/hudson/ExtensionListListener.java` | ソース | 拡張リスト変更リスナー |
| ExtensionListView.java | `core/src/main/java/hudson/ExtensionListView.java` | ソース | 拡張リストのLegacyビュー |
