📘 Vol.22	共通基底クラス BaseAction の作成（v2用）

## はじめに

前回の [Vol.20](https://qiita.com/juehara-crypto/items/3bb531ccff3aa36d4612) では、Struts2 アプリケーションの開発に必要な依存ライブラリを `pom.xml` に設定し、開発環境の基盤を整えました。

その上で今回の Vol.21 では、Struts2 における **共通基底クラス `BaseAction` の実装（v2）** を紹介します。

すべてのアクションで共通して行われる処理（ログ出力、セッション・リクエストの取得など）を `BaseAction` に集約することで、**コードの重複排除や保守性の向上**を図るのが目的です。

---

📘 **なお、本ブログでは以下の記事で `BaseAction` クラスの考え方や活用例についてすでに紹介しています：**

- [Vol.6：BaseActionクラスの詳細解説【Struts2の要】](https://qiita.com/juehara-crypto/items/a5f244a5f58597f33d06)
- [Vol.6.1：BaseAction の活用例【共通基底クラスでセッション管理を簡素化】](https://qiita.com/juehara-crypto/items/f4efded5b26e39e49928)

これらの記事では `BaseAction` のv1実装をベースに説明していましたが、**本記事では開発が進んだ v2 環境に対応する形で設計を整理・強化した `BaseAction` を再構築**しています。

---

Struts2 のアクション設計において **「すべての処理の起点」** となるこのクラスの改修は、今後のアクション開発の効率性と統一性を高める上で非常に重要なステップです。

---

### 目次
- 1.v1 との比較

- 2.実装のポイント（v2）

- 3.実装コード（BaseAction）

- 4.使用例（PostAction）

- 5.シーケンス図

- 6.まとめ

---

### 1. v1 との比較表

| 項目                       | v1 | v2 | 備考                       |
| ------------------------ | -- | -- | ------------------------ |
| `ActionSupport` 継承       | ✅  | ✅  | Struts2 の基本機能を提供         |
| `SessionAware` 実装        | ✅  | ✅  | セッション情報を取得可能             |
| `ServletRequestAware` 実装 | ✅  | ✅  | リクエスト情報を取得可能             |
| ロガー（Log4j）               | ✅  | ✅  | `BaseAction`で統一的なログ出力    |
| 共通実行メソッド `execute()`     | ✅  | ✅  | すべてのアクションで共通実行フロー        |
| 抽象メソッド `mainProc()`      | ✅  | ✅  | 各アクションはこのメソッドを実装         |
| ログディレクトリの作成              | ✅  | ✅  | 初期化ブロックで`logs/`ディレクトリを作成 |
| `getLoginUserId()` の定義   | ❌  | ✅  | セッションからログインユーザーID取得      |
| `getLoginUserName()` の定義 | ❌  | ✅  | セッションからログインユーザー名取得       |
| セッション/リクエストの再利用支援        | ❌  | ✅  | フィールド注入によりアクセスが容易になる     |


---

### 2. 実装のポイント（v2）
- ***全アクション共通のログ処理を `BaseAction` に実装***

- `SessionAware`, `ServletRequestAware` を実装してセッション・リクエスト取得を共通化

- セッションからログインユーザーの情報を取得する `getLoginUserId()` / `getLoginUserName()` を定義

- `execute()` は共通処理を行い、メイン処理は `mainProc()` に委譲

---

### 3. 実装コード（BaseAction.java）

```java
package com.company.bulletinboard.interceptor;

import java.io.File;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.interceptor.ServletRequestAware;
import org.apache.struts2.interceptor.SessionAware;
import com.opensymphony.xwork2.ActionSupport;

/**
 * 【Bulletinboard v2】共通基底クラス BaseAction
 * 各アクションが継承し、共通の処理（ログ出力、セッション管理、リクエスト管理）を担当
 */
public abstract class BaseAction extends ActionSupport implements ServletRequestAware, SessionAware {

    protected static final Logger logger = LogManager.getLogger(BaseAction.class);

    protected HttpServletRequest request;
    protected Map<String, Object> session;

    static {
        // ログディレクトリの作成（初回のみ）
        File logDir = new File("logs");
        if (!logDir.exists()) {
            logDir.mkdirs();
        }
    }

    @Override
    public void setServletRequest(HttpServletRequest request) {
        this.request = request;
        logger.debug("BaseAction: HttpServletRequest set");
    }

    @Override
    public void setSession(Map<String, Object> session) {
        this.session = session;
        logger.debug("BaseAction: Session set");
    }

    /**
     * ログインユーザーIDの取得（セッションから）
     */
    protected String getLoginUserId() {
        Object userId = session.get("user_id");
        return userId != null ? userId.toString() : null;
    }

    /**
     * ログインユーザー名の取得（セッションから）
     */
    protected String getLoginUserName() {
        Object userName = session.get("user_name");
        return userName != null ? userName.toString() : null;
    }

    /**
     * 実行メソッド：共通処理＋mainProc呼び出し
     */
    @Override
    public String execute() {
        String result = ERROR;

        logger.info("【処理開始】：" + this.getClass().getSimpleName());

        try {
            result = mainProc();
        } catch (Exception e) {
            logger.error("【エラー発生】：" + this.getClass().getSimpleName(), e);
        }

        logger.info("【処理終了】：" + this.getClass().getSimpleName());
        logger.debug("【Result】：" + result);

        return result;
    }

    /**
     * 各Actionで実装されるメイン処理
     */
    abstract protected String mainProc() throws Exception;
}

```

---

### 4. 使用例：PostAction クラス

```java
public class PostAction extends BaseAction {

    @Override
    protected String mainProc() throws Exception {
        String userId = getLoginUserId();
        logger.info("投稿者ID：" + userId);

        // 投稿処理...

        return SUCCESS;
    }
}

```

---

### 5. シーケンス図：BaseAction 実行フロー

```java
@startuml
actor User
participant "Struts2 フレームワーク" as Struts2
participant "SampleAction\n（BaseActionを継承）" as SampleAction
participant "BaseAction"
participant "DAO/Service"
database "DB" as DB

== リクエスト発行 ==
User -> Struts2 : HTTPリクエスト送信

== 実行対象アクションの決定 ==
Struts2 -> SampleAction : インスタンス生成

== セッション/リクエストの注入 ==
Struts2 -> SampleAction : setSession(session)
SampleAction -> BaseAction : setSession(session)\n（継承によりBaseActionが処理）
BaseAction -> BaseAction : this.session に保存

Struts2 -> SampleAction : setServletRequest(request)
SampleAction -> BaseAction : setServletRequest(request)\n（継承によりBaseActionが処理）
BaseAction -> BaseAction : this.request に保存

== 共通処理（BaseAction） ==
Struts2 -> BaseAction : execute() 呼び出し
BaseAction -> BaseAction : logger.info("処理開始")

== 個別処理（オーバーライド） ==
BaseAction -> SampleAction : mainProc() 呼び出し\n（SampleAction側でオーバーライド）

SampleAction -> DAO/Service : DBアクセス（例：スレッド取得）
DAO/Service -> DB : SQL実行
DB --> DAO/Service : 結果返却
DAO/Service --> SampleAction : 業務データ返却

SampleAction --> BaseAction : 処理結果（例："success"）

BaseAction -> BaseAction : logger.info("処理終了")
BaseAction -> Struts2 : 処理結果文字列（例："success"）

== レスポンス生成 ==
Struts2 -> User : JSP等で画面描画
@enduml

```

>📝 ***補足：セッション・リクエストの実体的な処理について***
上記のシーケンス図では、Struts2 から `SampleAction` に `setSession()` や `setServletRequest()` が呼び出されているように見えますが、**実際の処理は `BaseAction` 側で行われています。**
これは、`SampleAction` が `BaseAction` を継承しており、`BaseAction` が `SessionAware` および `ServletRequestAware` を実装しているためです。
したがって、Struts2 フレームワークは `SampleAction` に対してメソッドを呼び出しますが、**その処理ロジックは `BaseAction` に定義されているものが実行されます。**
継承関係により、共通のセッション・リクエスト管理がすべてのアクションで一元的に機能します。

---

### 6. まとめ
Struts2 アプリケーションでは、共通処理を抽象クラスに切り出すことで、以下のようなメリットが得られます：

- コードの重複を減らし、保守性が向上

- アクションごとの処理をシンプルに記述できる

- セッションやリクエストへのアクセスが容易になる

- ログ出力の統一により、デバッグ効率アップ

今後アクションを追加しても、`BaseAction` を継承するだけでログ・セッション・リクエスト処理を自動で引き継げます。ぜひご活用ください！

---


### ✅今後の予定

| Vol.       | タイトル案                          | 内容                                                                   |
| ---------- | ------------------------------ | -------------------------------------------------------------------- |
| **Vol.20** | 🔄 pom.xmlの依存ライブラリの自動解決設定      | EclipseのMavenプロジェクトでpom.xmlの依存関係を自動解決させる方法（Maven → Update Projectなど） |
| **Vol.21** | 共通基底クラス `BaseAction` の作成       | Struts2 のすべてのアクションで共通化される処理（セッション取得・ログ出力など）をまとめる                     |
| **Vol.22** | ログイン機能①：web.xmlの作成とStruts2起動確認 | `web.xml` の役割、Struts2フィルタの設定、起点としての重要性と動作確認                          |
| **Vol.23** | ログイン機能②：JSP画面の作成（ログインフォーム）     | `/WEB-INF/jsp/login.jsp` の作成、フォームタグの使用、Struts連携確認                    |
| **Vol.24** | ログイン機能③：struts.xmlのルーティング設定    | `struts.xml` におけるアクションマッピングの基本とログイン機能の設定                             |
| **Vol.25** | ログイン機能④：LoginActionクラスの作成      | ユーザーID・パスワードの受け取り／認証処理との接続／BaseAction継承の活用                           |
| **Vol.26** | ログイン機能⑤：DB接続クラスとDAO層の作成        | JDBCもしくはMyBatisの設定／`UserDao`の骨組み／テスト接続確認                             |
| **Vol.27** | ログイン機能⑥：DB準備（テーブル作成）           | ログイン用のユーザーテーブルを作成し、初期データ挿入まで                                         |
| **Vol.28** | ログイン機能⑦：ユーザー認証処理の実装            | DAOで取得したユーザー情報を用いてログイン成功・失敗の分岐を作成                                    |
| **Vol.29** | ログイン機能⑧：管理者ポータル画面の作成と遷移        | ログイン成功後に「管理者ポータル画面」に遷移／各管理機能（掲示板・ユーザー・スレッド・投稿）へのリンク設置                |
| **Vol.30** | 管理機能①：掲示板管理画面の一覧表示の実装          | 既存の掲示板データを管理者画面に一覧表示する／DAO・JSP連携の確認                                  |
| **Vol.31** | 管理機能②：掲示板作成機能の実装               | 掲示板作成フォームの作成／作成後に一覧に戻る処理の流れ／バリデーション準備                                |
| **Vol.32** | 管理機能③：掲示板編集機能の実装               | 編集対象掲示板の選択／情報の取得と更新／セッション引き継ぎとJSP反映                                  |
| **Vol.33** | 管理機能④：掲示板削除機能の実装               | 削除ボタンの配置／アクション・DAOによる物理削除処理と画面更新                                     |
| **Vol.34** | 管理機能⑤：ユーザー管理画面の一覧表示の実装         | 登録ユーザーを一覧表示／ユーザー編集・削除への遷移ボタン設置                                       |
| **Vol.35** | 管理機能⑥：ユーザー作成機能の実装              | 新規ユーザー追加フォーム／パスワード・権限などのバリデーション                                      |
| **Vol.36** | 管理機能⑦：ユーザー編集・削除機能の実装           | ユーザー情報の更新／削除による整合性チェックと処理                                            |
| **Vol.37** | 管理機能⑧：スレッド管理画面の一覧表示と削除機能       | 掲示板ごとのスレッド表示と削除（掲示板IDでの紐づけ確認）                                        |
| **Vol.38** | 管理機能⑨：投稿管理画面の一覧表示と削除機能         | 各スレッドに紐づく投稿を一覧で表示／不適切投稿の削除処理                                         |
| **Vol.39** | ログイン機能⑨：JSP表示にユーザー名を連携         | セッションから取得し、画面上にユーザー名を表示する方法の実装                                       |
| **Vol.40** | ログイン機能⑩：軽微なユニットテストとデバッグ活用      | EclipseのJUnit／デバッグビューの使い方／ログ確認方法                                     |
| **Vol.41** | 今後の拡張方針：一般ユーザーポータルと投稿機能の導入へ    | 一般ユーザー向け機能へと発展／掲示板閲覧→スレッド作成→投稿までの基本設計                                |

---