JavaフレームワークStruts入門:Web開発の基礎知識

JavaフレームワークStruts入門:Web開発の基礎知識を深掘りする

はじめに:Web開発の効率化とフレームワークの役割

今日のデジタル社会において、Webアプリケーションは私たちの生活やビジネスに不可欠な存在となっています。ECサイト、SNS、業務システム、情報ポータルなど、様々なWebアプリケーションが日々開発・運用されています。これらのWebアプリケーション開発を効率的かつ堅牢に進めるために、フレームワークと呼ばれるものが重要な役割を果たしています。

フレームワークとは、アプリケーション開発における構造や基盤を提供するものです。共通して必要となる機能(HTTPリクエスト/レスポンス処理、画面遷移制御、データベース連携など)があらかじめ用意されており、開発者はアプリケーション固有のビジネスロジックの実装に集中できます。これにより、開発期間の短縮、品質の均一化、保守性の向上といったメリットが得られます。

Java言語は、エンタープライズ分野を中心に多くのWebアプリケーション開発に利用されています。JavaでWebアプリケーションを開発するためのフレームワークも数多く存在しますが、その中でも長い歴史を持ち、多くのプロジェクトで利用されてきたのがApache Strutsです。本記事では、Struts、特に現在の主流であるStruts 2を中心に、その基本的な概念、アーキテクチャ、開発手法について、Web開発の基礎知識と関連付けながら詳細に解説していきます。Strutsを初めて学ぶ方、JavaによるWeb開発フレームワークに関心のある方にとって、理解の助けとなれば幸いです。

本記事は、読者がServlet/JSP、HTML、CSS、JavaScriptといったWeb開発の基本的な知識、およびJava言語の基本的なプログラミングスキルを有していることを前提としています。これらの基礎知識についても、Strutsの理解を深めるために簡単におさらいしながら進めていきます。

Web開発の基礎知識のおさらい

Strutsを学ぶ前に、Webアプリケーションがどのように動作するのか、そしてJavaによるWeb開発の基本について簡単におさらいしましょう。

Webアプリケーションの仕組み:クライアントとサーバー、HTTP

Webアプリケーションは、主に「クライアント」と「サーバー」という二つの要素から成り立っています。

  • クライアント: ユーザーが操作する側のプログラムで、通常はWebブラウザ(Chrome, Firefox, Safariなど)です。
  • サーバー: Webアプリケーションの本体が動作するコンピュータやプログラムです。クライアントからの要求(リクエスト)を受け付け、処理を行い、結果(レスポンス)を返します。

クライアントとサーバー間の通信には、HTTP(Hypertext Transfer Protocol)というプロトコルが使用されます。ユーザーがWebブラウザでURLを入力したり、リンクをクリックしたりすると、ブラウザは指定されたサーバーに対してHTTPリクエストを送信します。サーバーはリクエストを受け取り、要求された処理(Webページの表示、フォームデータの処理、データベースからの情報取得など)を実行し、その結果をHTTPレスポンスとしてブラウザに返します。ブラウザは受け取ったレスポンス(HTML、CSS、JavaScriptなどのデータ)を解釈し、ユーザーが目にする形で表示します。

JavaによるWeb開発:ServletとJSP

JavaでサーバーサイドのWebアプリケーションを開発するための基盤技術として、主にServletとJSPがあります。

  • Servlet: Javaで記述されたサーバーサイドプログラムです。HTTPリクエストを受け取り、Javaコードで動的な処理を実行し、HTTPレスポンスを生成します。ServletはJavaのクラスとして実装され、Webサーバー(正確にはServletコンテナ、例えばTomcatやJetty)によって管理・実行されます。ビジネスロジックの処理に適しています。
  • JSP (JavaServer Pages): HTMLの中にJavaコードを埋め込むことができる技術です。サーバー側で実行され、最終的にHTMLとしてクライアントに返されます。主に動的なWebページの表示に使われます。デザイン(View)の記述に適していますが、複雑なロジックを記述すると可読性や保守性が低下しがちです。

ServletとJSPを組み合わせることで、動的なWebページを作成できます。例えば、Servletでデータベースからデータを取得し、そのデータをJSPに渡して表示するといった連携が一般的です。

MVCアーキテクチャ:責務の分離

Webアプリケーション開発が複雑になるにつれて、コードが整理されずにスパゲッティ化してしまう問題が発生しやすくなります。これを解決し、開発効率、保守性、再利用性を向上させるための設計パターンとして、MVC(Model-View-Controller)が広く採用されています。

MVCはアプリケーションの構成要素を以下の3つの役割に分割します。

  • Model: アプリケーションが扱うデータ(ビジネスロジック、データベース連携など)を担当します。ViewやControllerから独立しており、データの状態や操作ロジックを管理します。
  • View: ユーザーインターフェース(画面表示)を担当します。Modelから渡されたデータを表示する役割を持ち、ユーザーからの入力(例えばフォーム送信)をControllerに伝えます。JSPやHTMLがこれに該当します。
  • Controller: クライアントからのリクエストを受け取り、適切なModelを呼び出して処理を実行させ、その結果を使って表示すべきViewを選択し、Viewに処理結果(データ)を渡します。リクエストとレスポンスの流れを制御する司令塔のような役割を果たします。Servletがこれに該当することが多いです。

MVCパターンを採用することで、それぞれの役割が明確になり、コードの見通しが良くなります。例えば、画面のデザインを変更したい場合はViewだけを修正すればよく、データの処理方法を変更したい場合はModelだけを修正すればよい、といったように、各要素が独立して開発・保守しやすくなります。

フレームワークの必要性

ServletとJSPだけでもWebアプリケーションは開発できますが、大規模かつ複雑なアプリケーションを開発する際には、以下のような課題が出てきます。

  • 定型処理の繰り返し: リクエストパラメータの取得、バリデーション、画面遷移制御など、どのWebアプリケーションでも共通して必要となる定型的な処理を毎回自分で書く必要がある。
  • 開発スタイルのばらつき: 開発者によってコードの書き方やディレクトリ構造がばらばらになりやすく、チーム開発や引き継ぎが困難になる。
  • 保守性の低下: ロジックと表示が混在したり、画面遷移が複雑になったりして、コードの変更やデバッグが難しくなる。
  • セキュリティ対策: セキュリティに関する考慮事項(XSS, CSRF, SQLインジェクションなど)を常に意識し、自前で対策を講じる必要がある。

これらの課題を解決するために、Webフレームワークが利用されます。フレームワークは、上述したような定型処理を抽象化・部品化して提供し、MVCなどの設計パターンを適用しやすい構造を提供します。また、多くのフレームワークには、セキュリティ対策、バリデーション機能、国際化(I18n)、ファイルアップロードなどの共通機能があらかじめ組み込まれています。これにより、開発者はビジネスロジックの実装に集中でき、開発生産性の向上とアプリケーションの品質向上が期待できます。

Strutsは、JavaによるWeb開発において、これらのフレームワークのメリットを享受するために生まれた代表的なフレームワークの一つです。

Strutsとは何か?

Apache Strutsは、JavaEE (Java Platform, Enterprise Edition) 環境で動作する、MVCモデルに基づいたオープンソースのWebアプリケーションフレームワークです。主にプレゼンテーション層(ユーザーインターフェースに関連する部分)の開発を支援することを目的としています。

Strutsの歴史:Struts 1からStruts 2へ

Strutsは2001年にApache Software FoundationからリリースされたStruts 1から始まりました。Struts 1は当時のJava Web開発における標準的なフレームワークの一つとして広く普及しました。サーブレットをController、JSPをView、そしてJavaBeansをModelとして利用するシンプルな構造を提供し、MVCパターンをJava Web開発に持ち込んだ功績は大きいと言えます。Struts 1はstruts-config.xmlという設定ファイルにアクションマッピングや画面遷移情報を記述するのが特徴でした。

しかし、Struts 1にはいくつかの課題もありました。例えば、Servlet APIに密結合している点、フォームデータとActionクラスの連携の複雑さ、柔軟性に欠けるInterceptor機能などが挙げられます。

これらの課題を克服し、より現代的な設計思想を取り入れて開発されたのが、Struts 2です。Struts 2は、もともとWebWorkという別のJava Webフレームワークをベースに開発されました。Struts 1とは互換性がなく、アーキテクチャも大きく異なります。Struts 2は、より柔軟なInterceptor機構、Value StackとOGNLによるデータアクセス、設定ファイルのシンプル化(アノテーションも利用可能)、テスト容易性の向上などを特徴としています。

現在、Strutsというと一般的にはStruts 2を指します。Struts 1は既に公式なサポートが終了しており、新規開発で利用されることはほとんどありません。本記事でも、以降はStruts 2について解説を進めます。

Struts 2が実現するMVCアーキテクチャ

Struts 2は、MVCアーキテクチャを徹底的に適用しています。Struts 2における各要素の役割は以下のようになります。

  • Model: ビジネスロジックやデータ処理は、Struts 2のActionクラス内や、Actionクラスから呼び出されるサービス層/ドメイン層のJavaクラスで実装します。Struts 2自体はModelの実装方法を強制しませんが、ActionクラスがModelの一部(データや処理結果)を持つことが一般的です。
  • View: ユーザーインターフェースの表示を担当します。主にJSPを使用しますが、VelocityやFreeMarkerといったテンプレートエンジンも利用可能です。Struts 2は、View内で簡単にModelやActionのデータにアクセスするための仕組み(Value Stack, OGNL, Strutsタグライブラリ)を提供します。
  • Controller: クライアントからのリクエストを受け取り、適切な処理を実行し、結果をユーザーに返すまでの一連の流れを制御します。Struts 2では、この役割をActionクラスとFilterDispatcher (またはStrutsPrepareAndExecuteFilter) というServletフィルターが担います。

リクエスト処理のフローは、Struts 2のFilterDispatcher/StrutsPrepareAndExecuteFilterが全てのリクエストを捕捉することから始まります。このフィルターが、設定ファイル(通常 struts.xml)を参照して、リクエストパスに対応するActionクラスを特定し、インスタンスを生成します。その後、Actionクラスのメソッドを実行し、その結果に基づいて表示すべきView(JSPなど)を決定します。この処理の中で、様々な機能を持つInterceptorがActionの実行前後に割り込み、共通処理(パラメータ設定、バリデーション、ファイルアップロード、セッション管理など)を実行します。

Struts 2の主要な構成要素

Struts 2のアーキテクチャを理解するために、主要な構成要素を把握しておきましょう。

  1. FilterDispatcher / StrutsPrepareAndExecuteFilter: Struts 2アプリケーションのエントリーポイントとなるServletフィルターです。Web.xmlに設定され、Struts 2が処理すべき全てのリクエストを捕捉します。リクエストの前処理、Struts 2コンテキストの準備、Actionの実行、レスポンスの生成といった一連の処理を制御します。Struts 2.1以降ではStrutsPrepareAndExecuteFilterが推奨されています。
  2. Action: クライアントからの特定のリクエストに対応する処理を実行するJavaクラスです。ビジネスロジックを呼び出したり、データを準備したりします。Actionクラスは通常、com.opensymphony.xwork2.ActionSupport クラスを継承するか、com.opensymphony.xwork2.Action インターフェースを実装します。execute() メソッドなどがリクエスト処理のエントリーポイントとなります。Actionメソッドの戻り値(文字列)が、次に表示すべきView(Result)を決定します。
  3. Result: Actionの実行結果に基づいて表示されるリソース(JSPファイル、別のActionへのリダイレクトなど)を定義します。Actionメソッドの戻り値(例: "success", "error", "input") にマッピングされます。struts.xml で定義します。
  4. Interceptor: Actionの実行前や実行後に割り込んで共通処理を行うためのクラスです。リクエストパラメータの取得、バリデーション、ファイルアップロード、ワークフロー制御、例外処理、ロギング、認証/認可など、様々な機能を提供します。複数のInterceptorを組み合わせて「Interceptorチェーン」を構成し、Actionの実行パイプラインを形成します。Struts 2の強力な特徴の一つです。
  5. Value Stack: Actionクラスのインスタンスや、Interceptorによって設定されたデータなどを格納するための特別なスタック構造です。リクエストスコープで管理され、Actionクラスのプロパティなどが自動的にValue Stackのトップに配置されます。JSPなどのViewから、このValue Stackに格納されたデータにアクセスします。
  6. OGNL (Object-Graph Navigation Language): Javaオブジェクトのプロパティにアクセスしたり、メソッドを呼び出したりするための強力な式言語です。Struts 2では、Value Stackに格納されたデータへのアクセスや、JSP内でのデータ表示、フォームデータとActionプロパティのマッピングなどにOGNLが広く利用されます。
  7. Configuration: Struts 2アプリケーション全体の設定を行うためのファイルです。主に struts.xml を使用します。どのURLパターンがどのActionクラスに対応するか、Action実行後にどのResultに遷移するか、どのようなInterceptorチェーンを適用するかなどを定義します。XML形式で記述するのが一般的ですが、アノテーションやJavaコードによる設定も可能です。

これらの要素が連携することで、Struts 2は柔軟で拡張性の高いWebアプリケーション開発環境を提供します。

Struts 2のアーキテクチャ詳細

ここからは、Struts 2のアーキテクチャをもう少し深く掘り下げてみましょう。

リクエスト処理フロー

Struts 2におけるHTTPリクエストの基本的な処理フローは以下のようになります。

  1. リクエスト受信: WebブラウザからWebサーバー(例えばTomcat)にHTTPリクエストが送信されます。
  2. FilterDispatcher / StrutsPrepareAndExecuteFilter: Web.xmlに設定されたStruts 2のフィルターがこのリクエストを捕捉します。
  3. 前処理: フィルターは、Struts 2コンテキストの初期化や、HTTPリクエスト/レスポンスオブジェクトのラップなどの前処理を行います。
  4. ActionMapper: リクエストのURLパスやメソッド(GET/POSTなど)に基づいて、実行すべきActionの名前や名前空間を決定します。
  5. Configuration Manager: ActionMapperによって決定されたActionの名前などをもとに、struts.xmlなどの設定情報を読み込み、対応するActionクラスやInterceptorチェーン、Result情報を取得します。
  6. ActionProxyFactory: ActionとInterceptorチェーンの実行を管理するActionProxyを生成します。
  7. ActionProxy: Actionの実際の実行を委譲されるオブジェクトです。Interceptorチェーンの実行とActionの実行を制御します。
  8. Interceptorチェーン実行: ActionProxyは、設定されたInterceptorチェーンを順番に実行します。各InterceptorはActionの実行前になんらかの処理を行い、次のInterceptorまたはAction自身に処理を渡します。例えば、ParametersInterceptorはリクエストパラメータをActionプロパティに設定し、ValidationInterceptorは入力値のバリデーションを行います。
  9. Action実行: Interceptorチェーンの最後にActionクラスの指定されたメソッド(デフォルトはexecute())が実行されます。Actionメソッドはビジネスロジックを呼び出すなどの処理を行い、結果を文字列で返します。
  10. Value Stack更新: Actionクラスの実行中に、Actionクラスのプロパティやメソッドの戻り値などがValue Stackに格納・更新されます。
  11. Interceptorチェーン逆実行: Actionの実行後、Interceptorチェーンは実行時とは逆の順序で再び実行されます。各InterceptorはAction実行後の処理を行うことができます。例えば、ExceptionMappingInterceptorは例外処理を行い、WorkflowInterceptorはバリデーションエラー時の遷移を制御します。
  12. Resultの決定: Actionメソッドの戻り値(文字列、例: "success")と、ActionProxyに設定されたResult情報に基づいて、実際に表示すべきView(JSPなど)が決定されます。
  13. Result実行: 決定されたResultオブジェクトが実行されます。例えば、DispatcherResultであれば指定されたJSPにフォワードし、RedirectResultであれば指定されたURLにリダイレクトします。
  14. Viewレンダリング: ResultがJSPをフォワードした場合、JSPはValue Stackに格納されたデータをOGNLやStrutsタグライブラリを使って参照し、動的なHTMLを生成します。
  15. レスポンス生成: 生成されたHTML(またはリダイレクト命令など)がHTTPレスポンスとしてWebブラウザに返されます。
  16. レスポンス表示: WebブラウザがHTTPレスポンスを受け取り、コンテンツを表示します。

このフローの中で、特にInterceptorチェーンがStruts 2の柔軟性を高めています。様々な共通機能をInterceptorとしてプラグインすることで、Actionクラスはビジネスロジックの実装に専念できます。

Struts.xmlの設定

Struts 2アプリケーションの主要な設定は、通常 src/main/resources/struts.xml に記述します。このファイルは、Struts 2がリクエストを処理する際の振る舞いを定義します。

基本的な struts.xml の構造は以下のようになります。

“`xml

<!-- Struts 2のデフォルト設定を読み込む -->
<include file="struts-default.xml"/>

<!-- アプリケーション固有の設定を記述 -->
<package name="default" namespace="/" extends="struts-default">

    <!-- アクション定義 -->
    <action name="hello" class="com.example.action.HelloAction">
        <!-- 実行結果に対する遷移先(Result)を定義 -->
        <result name="success">/WEB-INF/jsp/hello.jsp</result>
    </action>

    <!-- 別のAction定義 -->
    <action name="welcome" class="com.example.action.WelcomeAction">
        <result name="success">/WEB-INF/jsp/welcome.jsp</result>
        <result name="input">/WEB-INF/jsp/welcome-form.jsp</result>
        <result name="error">/WEB-INF/jsp/error.jsp</result>
    </action>

</package>

<!-- 別の名前空間を持つパッケージ -->
<package name="admin" namespace="/admin" extends="struts-default">
    <action name="dashboard" class="com.example.action.admin.DashboardAction">
        <result name="success">/WEB-INF/jsp/admin/dashboard.jsp</result>
    </action>
</package>


“`

  • <struts>: ルート要素です。
  • <include file="struts-default.xml"/>: Struts 2が提供するデフォルトのInterceptor定義やResultタイプ定義などを読み込みます。通常必須です。
  • <package>: 複数のAction定義をまとめるための論理的なグループです。
    • name: パッケージの名前(一意である必要あり)。
    • namespace: このパッケージ内のActionが応答するURLの名前空間を定義します。/ はルートパスを示します。/admin/admin/* のURLに対応します。
    • extends: 他のパッケージの設定を継承できます。通常 struts-default を継承し、デフォルトのInterceptorチェーンやResultタイプを利用可能にします。
  • <action>: 特定のリクエストパスに対するActionクラスとその振る舞いを定義します。
    • name: リクエストパスの末尾部分に対応する名前です。例えば、namespaceが / でaction nameが hello なら、/hello.action または /hello というURLに対応します(URLサフィックスは設定で変更可能)。
    • class: 実行されるActionクラスの完全修飾名です。
    • method: (省略可能)Actionクラス内で実行されるメソッド名を指定します。省略した場合は通常 execute メソッドが実行されます。
  • <result>: Actionメソッドの実行結果(戻り値の文字列)に対応する遷移先を定義します。
    • name: Actionメソッドの戻り値と一致させる文字列です(例: "success", "error", "input")。
    • 要素の中身: 遷移先のパスを指定します。これは通常JSPファイルへのパスですが、別のActionへのリダイレクトなども設定可能です。デフォルトのタイプはDispatcher(JSPへのフォワード)です。

このように、struts.xml を編集することで、URLとActionクラス、そしてActionの実行結果と表示するView(JSP)のマッピングを宣言的に記述できます。これにより、Javaコード(Actionクラス)から画面遷移のロジックを分離でき、保守性が向上します。

Struts 2.5以降では、XML設定に加えて、クラスパス上のXMLファイル (struts-plugin.xml) やJavaコード (@Namespace, @Action, @Result などのアノテーション) による設定もサポートされています。アノテーションを使用すると、Actionクラスの定義と関連する設定をまとめて記述できるため、設定ファイルが不要になる場合もあり、開発効率が向上します。ただし、大規模なアプリケーションや、Actionの数が非常に多い場合は、XMLで一元管理する方が見通しが良い場合もあります。

Interceptorチェーンの役割とカスタマイズ

InterceptorはStruts 2の強力な機能であり、リクエスト処理のパイプラインに様々な共通処理を組み込むことを可能にします。Interceptorは、Actionクラスの実行前、実行後、そしてResultの実行前後に処理を割り込ませることができます。

Struts 2は多くの標準Interceptorを提供しています。代表的なものには以下のようなものがあります。

  • params: HTTPリクエストパラメータをActionクラスのプロパティに自動的に設定します。
  • modelDriven: ActionクラスがModelオブジェクトを持つ場合に、そのModelオブジェクトのプロパティにパラメータを設定します。
  • fileUpload: ファイルアップロード処理を行います。
  • validation: Actionクラスに定義されたバリデーションルールに基づいて入力値を検証します。
  • workflow: バリデーションエラーが発生した場合に、Actionメソッドの実行をスキップして”input” Resultに遷移させるといったワークフロー制御を行います。
  • exception: Action実行中に発生した例外をキャッチし、エラーページに遷移させるといった例外処理を行います。
  • staticParams: struts.xmlで静的に設定されたパラメータをActionプロパティに設定します。

これらの標準Interceptorは、デフォルトのInterceptorチェーン(basicStackdefaultStackなど)としてまとめられています。struts-default.xmlで定義されているdefaultStackは、多くの一般的なWebアプリケーション開発で必要とされるInterceptorを網羅しています。<package extends="struts-default"> とすることで、このデフォルトスタックを継承し利用できるようになります。

<action> 要素内で <interceptor-ref name="..." /> を使用して、特定のアクションに対して使用するInterceptorやInterceptorチェーンをカスタマイズすることも可能です。

“`xml





10485760


<result name="success">/WEB-INF/jsp/success.jsp</result>
<result name="input">/WEB-INF/jsp/input.jsp</result>


“`

独自のInterceptorを作成してInterceptorチェーンに組み込むことも可能です。これにより、アプリケーション固有の共通処理(例えば、ユーザー認証、アクセスログ記録など)をActionクラスから分離し、再利用可能な形で実装できます。

Value StackとOGNLの活用

Struts 2のView(JSPなど)からActionクラスやModelのデータにアクセスするための重要な仕組みがValue StackとOGNLです。

  • Value Stack: リクエストごとに生成される、オブジェクトを格納するためのスタック構造です。ActionクラスのインスタンスはValue Stackのトップに自動的にプッシュされます。また、Actionが使用するModelオブジェクトなどもValue Stackにプッシュできます。Interceptorによって追加されたデータも格納されます。JSPからは、このValue Stackを検索して目的のデータにアクセスします。
  • OGNL: Javaオブジェクトのプロパティアクセスやメソッド呼び出しを簡潔に記述できる式言語です。Struts 2では、JSP内でValue Stackに格納されたデータにアクセスする際にOGNL式を使用します。

JSP内でOGNL式を使うには、Struts 2が提供するOGNLタグや属性を利用します。最も基本的なOGNL式の評価には <s:property value="..."/> タグを使用します。

例:Actionクラスに userName というプロパティがある場合。

“`java
// com.example.action.WelcomeAction.java
public class WelcomeAction extends ActionSupport {
private String userName;

public String getUserName() {
    return userName;
}

public void setUserName(String userName) {
    this.userName = userName;
}

@Override
public String execute() {
    if (userName == null || userName.trim().isEmpty()) {
        userName = "Guest"; // デフォルト値
    }
    return SUCCESS;
}

}
“`

“`jsp
<%– /WEB-INF/jsp/welcome.jsp –%>
<%@ taglib prefix=”s” uri=”/struts-tags” %>


Welcome

Welcome, !

<%-- Actionクラスのメソッド呼び出しも可能 --%>

Message:

<%-- ネストされたプロパティへのアクセス --%>
<%-- Actionクラスに user オブジェクトがあり、その中に address というプロパティがある場合 --%>
<%--

Address:

–%>


“`

<s:property value="userName"/> というOGNL式は、Value Stackのトップ(この場合は WelcomeAction インスタンス)から userName というプロパティの値を取得して表示します。OGNLは、JavaBeansのgetter/setterメソッド規約に従ってプロパティにアクセスします(例: userName プロパティは getUserName() メソッドに対応)。

OGNLは非常に強力で、プロパティチェーン (user.address.city)、リストやマップの要素アクセス (list[0], map['key'])、メソッド呼び出し (user.getName())、静的メンバーへのアクセス、条件式など、様々な表現が可能です。

Value StackとOGNLの組み合わせにより、JSPはActionクラスのデータを簡単に参照・表示できます。また、Struts 2のフォームタグ(<s:textfield name="userName"/> など)を使用すると、フォーム入力値とActionプロパティ間のデータのバインディングも自動的に行われます。これは、リクエストパラメータ名 (userName) がOGNL式として評価され、Value Stack上の対応するプロパティに値が設定されるという仕組みに基づいています。

Struts 2を使った基本的なWebアプリケーション開発

Struts 2の基本的なアーキテクチャを理解したところで、実際に簡単なWebアプリケーションを開発する手順を見ていきましょう。ここでは、Mavenを使ったプロジェクト管理を前提とします。

開発環境構築

Struts 2アプリケーションを開発するには、以下の環境が必要です。

  1. JDK (Java Development Kit): Javaのコンパイル・実行環境。
  2. IDE (統合開発環境): Eclipse, IntelliJ IDEA, NetBeansなど。Mavenとの連携機能があると便利です。
  3. Apache Maven: プロジェクトのビルド、依存関係管理、デプロイなどを効率化するためのツール。Struts 2はMavenリポジトリで公開されています。
  4. Servletコンテナ: Webアプリケーションを配置して実行するためのサーバー。Apache Tomcatなどが一般的です。

Mavenがインストールされ、環境変数 JAVA_HOMEM2_HOME が適切に設定されていることを確認してください。

Mavenプロジェクト作成

コマンドラインまたはIDEのMavenプロジェクト作成機能を使って、シンプルなWebアプリケーションプロジェクトを作成します。

bash
mvn archetype:generate \
-DgroupId=com.example \
-DartifactId=mywebapp \
-DarchetypeArtifactId=maven-archetype-webapp \
-DinteractiveMode=false

これにより、mywebappというディレクトリが作成され、Mavenプロジェクトの基本的な構造(src/main/webapp, pom.xmlなど)が生成されます。

pom.xmlの設定

生成されたpom.xmlに、Struts 2の依存関係と、Servlet API、JSP APIの依存関係を追加します。また、MavenコンパイラプラグインやWARプラグインの設定も行います。

“`xml
4.0.0
com.example
mywebapp war 1.0-SNAPSHOT
mywebapp Maven Webapp
http://maven.apache.org

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <struts2.version>2.5.30</struts2.version> <!-- 使用したいStruts 2のバージョンを指定 -->
</properties>

<dependencies>
    <!-- Struts 2 Core -->
    <dependency>
        <groupId>org.apache.struts</groupId>
        <artifactId>struts2-core</artifactId>
        <version>${struts2.version}</version>
    </dependency>

    <!-- Servlet API (provided scope: コンテナが提供するためWARに含めない) -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version> <!-- 使用するServletコンテナのバージョンに合わせる -->
        <scope>provided</scope>
    </dependency>

    <!-- JSP API (provided scope) -->
    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>javax.servlet.jsp-api</artifactId>
        <version>2.3.3</version> <!-- 使用するServletコンテナのバージョンに合わせる -->
        <scope>provided</scope>
    </dependency>

    <!-- JUnit for testing (optional) -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.2</version>
        <scope>test</scope>
    </dependency>
</dependencies>

<build>
    <finalName>mywebapp</finalName>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.1</version>
            <configuration>
                <source>${maven.compiler.source}</source>
                <target>${maven.compiler.target}</target>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-war-plugin</artifactId>
            <version>3.3.1</version>
            <configuration>
                <failOnMissingWebXml>false</failOnMissingWebXml> <!-- web.xmlがない場合でもビルドを許可 -->
            </configuration>
        </plugin>
    </plugins>
</build>

“`

struts2.version, javax.servlet-api のバージョンは、使用したいStruts 2のバージョンやServletコンテナのバージョンに合わせて適宜変更してください。

web.xmlの設定

Struts 2がリクエストを捕捉するためには、src/main/webapp/WEB-INF/web.xmlにStruts 2のフィルターを設定する必要があります。

“`xml


Struts 2 Web Application

<!-- Struts 2 Filter -->
<filter>
    <filter-name>struts2</filter-name>
    <!-- Struts 2.1以降で推奨されるフィルター -->
    <filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class>
    <!-- Struts 2.0.x以前のフィルター -->
    <!-- <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class> -->
</filter>

<filter-mapping>
    <filter-name>struts2</filter-name>
    <!-- 全てのリクエストをStruts 2フィルターに送る -->
    <url-pattern>/*</url-pattern>
</filter-mapping>

<welcome-file-list>
    <welcome-file>index.html</welcome-file> <!-- もしくは index.jsp など -->
</welcome-file-list>


“`

<filter-class> 要素で、Struts 2が提供するフィルタークラスを指定します。<filter-mapping> 要素で、どのURLパターンに対するリクエストをこのフィルターで処理するかを指定します。ここでは /* としているため、このWebアプリケーションへの全てのリクエストがStruts 2フィルターによって処理されます。

簡単な「Hello World」アプリケーションの作成

ユーザーにメッセージを表示する簡単な「Hello World」アプリケーションを作成します。

  1. Actionクラスの作成:
    src/main/java/com/example/action ディレクトリを作成し、HelloAction.java を作成します。

    “`java
    // src/main/java/com/example/action/HelloAction.java
    package com.example.action;

    import com.opensymphony.xwork2.ActionSupport;

    // ActionSupportを継承すると、execute()メソッドや入力値のバリデーションなど、多くの便利機能が利用できます。
    public class HelloAction extends ActionSupport {

    // Actionメソッド。通常、リクエスト処理のメインロジックを記述します。
    // デフォルトではexecute()メソッドが実行されます。
    // このメソッドの戻り値(文字列)が、struts.xmlで定義されたResultの名前とマッピングされます。
    @Override
    public String execute() {
        System.out.println("HelloAction executed!");
        // "success"という結果名を返す。struts.xmlでこの結果名に対応する遷移先が定義されます。
        return SUCCESS; // ActionSupportクラスで定義されている定数 "success"
    }
    
    // Actionクラスのプロパティは、Value Stackに自動的にプッシュされ、JSPからOGNLでアクセスできます。
    // private String message = "Hello, Struts 2 World!";
    // public String getMessage() { return message; }
    

    }
    “`

  2. JSP (View) の作成:
    src/main/webapp/WEB-INF/jsp ディレクトリを作成し、hello.jsp を作成します。WEB-INF ディレクトリ以下に置くことで、Webブラウザから直接アクセスされるのを防ぎます。

    jsp
    <%-- src/main/webapp/WEB-INF/jsp/hello.jsp --%>
    <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
    <%@ taglib prefix="s" uri="/struts-tags" %> <%-- Struts 2タグライブラリを使用可能にする --%>
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>Hello Struts 2</title>
    </head>
    <body>
    <h1><s:text name="greeting"/></h1> <%-- Struts 2のテキストタグ。プロパティファイルからメッセージを取得可能 -->
    <%-- <s:property value="message"/> --%> <%-- もしActionにmessageプロパティがあれば表示 -->
    </body>
    </html>

    <%@ taglib prefix="s" uri="/struts-tags" %> 行でStruts 2タグライブラリをインポートします。これにより、<s:...> 形式のStrutsタグが使用可能になります。<s:text name="greeting"/> は、プロパティファイルに定義されたキー "greeting" に対応するテキストを表示します。

  3. プロパティファイルの作成 (国際化対応の例):
    メッセージをプロパティファイルで管理することで、国際化(I18n)に対応しやすくなります。
    src/main/resources ディレクトリを作成し、package.properties または Actionクラス名.properties (例: HelloAction.properties) を作成します。

    “`properties

    src/main/resources/package.properties

    greeting=Hello, Struts 2 World from properties!
    “`

    Struts 2はデフォルトでpackage.propertiesやActionクラス名に対応する.propertiesファイルをクラスパスから探します。ここで定義したキーは、<s:text name="greeting"/> のようにStrutsタグで参照できます。

  4. struts.xmlの設定:
    src/main/resources ディレクトリに struts.xml を作成し、上記HelloActionとhello.jspのマッピングを定義します。

    “`xml
    <?xml version=”1.0″ encoding=”UTF-8″?>
    <!DOCTYPE struts PUBLIC
    “-//Apache Software Foundation//DTD Struts Configuration 2.5//EN”
    “http://struts.apache.org/dtds/struts-2.5.dtd”>

    <include file="struts-default.xml"/>
    
    <package name="hello" namespace="/" extends="struts-default">
        <action name="hello" class="com.example.action.HelloAction">
            <!-- Actionメソッドが"success"を返した場合、/WEB-INF/jsp/hello.jspにフォワードする -->
            <result name="success">/WEB-INF/jsp/hello.jsp</result>
        </action>
    </package>
    


    “`

    この設定により、/hello (または /hello.action) というリクエストが来た場合に、com.example.action.HelloAction クラスの execute() メソッドが実行され、その戻り値が "success" であれば、/WEB-INF/jsp/hello.jsp が表示されるようになります。

  5. warファイル作成とデプロイ:
    プロジェクトのルートディレクトリで以下のMavenコマンドを実行し、warファイルを作成します。

    bash
    mvn clean package

    target ディレクトリに mywebapp.war というファイルが生成されます。このwarファイルを、TomcatなどのServletコンテナの webapps ディレクトリに配置(デプロイ)します。

  6. 動作確認:
    Servletコンテナを起動し、Webブラウザで以下のURLにアクセスします。

    http://localhost:8080/mywebapp/hello

    localhost:8080 はServletコンテナのアドレスとポート、mywebapp はwarファイル名に基づいたコンテキストパスです。環境によって異なります。)

    正しく設定されていれば、「Hello, Struts 2 World from properties!」と表示されたページが表示されるはずです。

この基本的な「Hello World」アプリケーションを通じて、Struts 2におけるAction、Result、struts.xml、JSP、そしてValue Stack/OGNL(今回はプロパティファイルからのテキスト取得という形で間接的に利用)の連携の基本を体験できます。

フォーム入力と値の取得・表示

次に、フォームから入力された値をActionクラスで受け取り、別のページで表示する例を見てみましょう。

  1. 入力フォーム用JSPの作成:
    src/main/webapp/WEB-INF/jsp ディレクトリに input.jsp を作成します。Struts 2のフォームタグを使用します。

    “`jsp
    <%– src/main/webapp/WEB-INF/jsp/input.jsp –%>
    <%@ page language=”java” contentType=”text/html; charset=UTF-8″ pageEncoding=”UTF-8″%>
    <%@ taglib prefix=”s” uri=”/struts-tags” %>
    <!DOCTYPE html>



    Input Form

    Enter Your Name

    <%-- action属性でフォーム送信先のアクション名を指定 --%>
    <s:form action="greet">
        <%-- name属性は、Actionクラスのプロパティ名に対応させる --%>
        <s:textfield name="userName" label="Name"/>
        <s:submit value="Submit"/>
    </s:form>
    



    “`

    <s:form action="greet"> は、フォームが送信されたときに /greet (または /greet.action) というURLにリクエストを送信するHTMLフォームタグ <form> を生成します。
    <s:textfield name="userName" label="Name"/> は、テキスト入力フィールド <input type="text" name="userName"> とラベル <label> を生成します。ここで重要なのは name="userName" です。このフォームに入力された値は、リクエストパラメータ userName としてサーバーに送信されます。

  2. データを受け取るActionクラスの作成:
    src/main/java/com/example/action ディレクトリに GreetAction.java を作成します。Actionクラスに userName というプロパティを定義し、getter/setterメソッドを用意します。

    “`java
    // src/main/java/com/example/action/GreetAction.java
    package com.example.action;

    import com.opensymphony.xwork2.ActionSupport;

    public class GreetAction extends ActionSupport {

    // フォームから渡される値を受け取るためのプロパティとgetter/setter
    private String userName;
    
    public String getUserName() {
        return userName;
    }
    
    // Struts 2のParametersInterceptorが、リクエストパラメータ"userName"の値をこのsetterを使って設定します。
    public void setUserName(String userName) {
        this.userName = userName;
    }
    
    // 表示メッセージを保持するプロパティ (任意)
    private String greetingMessage;
    
    public String getGreetingMessage() {
        return greetingMessage;
    }
    
    @Override
    public String execute() {
        // userNameプロパティには、フォームから入力された値が自動的に設定されています。
        if (userName == null || userName.trim().isEmpty()) {
             greetingMessage = "Hello, Guest!";
        } else {
             greetingMessage = "Hello, " + userName + "!";
        }
        return SUCCESS; // 成功時は"success"を返す
    }
    

    }
    “`

    userName プロパティに対する setUserName() メソッドは非常に重要です。Struts 2の標準Interceptorである ParametersInterceptor が、HTTPリクエストパラメータの中からプロパティ名と一致するもの(この場合は userName)を探し、自動的にこのsetterメソッドを呼び出して値を設定してくれます。ActionインスタンスはValue Stackのトップにあるため、OGNLを介してプロパティにアクセスできる仕組みがここでも活用されています。

  3. 結果表示用JSPの作成:
    src/main/webapp/WEB-INF/jsp ディレクトリに greet.jsp を作成します。Actionクラスのプロパティ(Value Stack上のデータ)を表示します。

    “`jsp
    <%– src/main/webapp/WEB-INF/jsp/greet.jsp –%>
    <%@ page language=”java” contentType=”text/html; charset=UTF-8″ pageEncoding=”UTF-8″%>
    <%@ taglib prefix=”s” uri=”/struts-tags” %>
    <!DOCTYPE html>



    Greeting


    <%– GreetActionのgreetingMessageプロパティの値を表示 –%>

    <p><a href="input.action">Go Back</a></p> <%-- 入力フォームに戻るリンク --%>
    



    “`

    <s:property value="greetingMessage"/> で、Value Stack(この場合は GreetAction インスタンス)から greetingMessage プロパティの値を取得して表示します。

  4. struts.xmlの設定:
    struts.xml に新しいAction定義を追加します。

    “`xml
    <?xml version=”1.0″ encoding=”UTF-8″?>
    <!DOCTYPE struts PUBLIC
    “-//Apache Software Foundation//DTD Struts Configuration 2.5//EN”
    “http://struts.apache.org/dtds/struts-2.5.dtd”>

    <include file="struts-default.xml"/>
    
    <package name="mypackage" namespace="/" extends="struts-default">
    
        <!-- 入力フォームを表示するアクション (直接JSPにフォワードしても良いが、Actionを経由する方がStrutsらしい) -->
        <action name="input">
            <result name="success">/WEB-INF/jsp/input.jsp</result>
        </action>
    
        <!-- フォーム送信を受け付け、結果を表示するアクション -->
        <action name="greet" class="com.example.action.GreetAction">
            <!-- execute()が"success"を返した場合、greet.jspにフォワード -->
            <result name="success">/WEB-INF/jsp/greet.jsp</result>
            <!-- 入力エラーなどがあった場合に戻るページ (バリデーションを実装する際に使用) -->
            <result name="input">/WEB-INF/jsp/input.jsp</result>
        </action>
    
    </package>
    


    ``
    入力フォームを表示するための
    /inputAction (input.actionURLに対応) と、フォームデータを受け付けて処理する/greetAction (greet.action` URLに対応) を定義しました。

  5. ビルドとデプロイ、動作確認:
    再度 mvn clean package でwarファイルをビルドし、Tomcatにデプロイします。
    ブラウザで http://localhost:8080/mywebapp/input にアクセスします。
    入力フォームが表示されるので、名前を入力してSubmitボタンをクリックします。
    入力した名前を含むメッセージが表示される画面に遷移するはずです。

この例で、Struts 2のフォームタグ、Actionクラスのプロパティとリクエストパラメータの自動マッピング(ParametersInterceptorによる)、Value Stack、OGNLを利用したデータの表示といった、Struts 2の基本的なデータフローを理解できたかと思います。

バリデーションの実装

Webアプリケーションにおいて、ユーザーからの入力値の検証(バリデーション)は必須です。Struts 2は強力なバリデーション機能を提供しており、Actionクラスや関連ファイルに検証ルールを記述することで、Interceptorが自動的にバリデーションを実行してくれます。

Struts 2でのバリデーションの方法はいくつかありますが、最も一般的なのはXMLファイルで検証ルールを定義する方法です。

  1. バリデーションXMLファイルの作成:
    バリデーションルールを定義するXMLファイルは、バリデーションを行いたいActionクラスと同じパッケージに配置し、ファイル名を ActionClassName-validation.xml とします。
    例えば、GreetAction.javauserName フィールドが必須であることを検証する場合、src/main/resources/com/example/action ディレクトリに GreetAction-validation.xml を作成します。

    xml
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE validators PUBLIC
    "-//Apache Software Foundation//DTD Struts Validator 1.3//EN"
    "http://struts.apache.org/dtds/validators-1.3.dtd">
    <validators>
    <!-- userNameプロパティに対するバリデーションルール -->
    <field name="userName">
    <!-- requiredstringバリデーター:文字列が空でないことを検証 -->
    <field-validator type="requiredstring">
    <!-- 検証エラー時のメッセージ -->
    <message>Your name is required.</message>
    </field-validator>
    <!-- stringlengthバリデーター:文字列の長さを検証 -->
    <field-validator type="stringlength">
    <param name="maxLength">50</param>
    <message key="userName.too.long">Name must be less than ${maxLength} characters.</message>
    </field-validator>
    </field>
    </validators>

    • <validators>: ルート要素。
    • <field name="propertyName">: バリデーション対象のActionプロパティ名を指定。
    • <field-validator type="validatorName">: 適用するバリデーターのタイプを指定。requiredstring は必須入力チェック、stringlength は文字数チェックなど、様々な標準バリデーターがあります。
    • <param name="parameterName">parameterValue</param>: バリデーターのパラメータを指定(例: maxLength)。
    • <message>errorMessage</message>: 検証エラーが発生した際に表示するエラーメッセージ。<message key="..."/> とすれば、プロパティファイルからメッセージを取得できます。

    Struts 2の ValidationInterceptor が、Action実行前にこのXMLファイルを読み込み、定義されたルールに従ってバリデーションを実行します。

  2. エラーメッセージの表示:
    バリデーションエラーが発生した場合、ValidationInterceptorWorkflowInterceptor の連携により、Actionメソッドは実行されずに input Resultに遷移します。このとき、エラーメッセージは自動的にStruts 2のフィールドエラー (FieldErrors) としてValue Stackに格納されます。
    入力フォーム (input.jsp) に、これらのエラーメッセージを表示するためのStrutsタグを追加します。

    “`jsp
    <%– src/main/webapp/WEB-INF/jsp/input.jsp (修正) –%>
    <%@ page language=”java” contentType=”text/html; charset=UTF-8″ pageEncoding=”UTF-8″%>
    <%@ taglib prefix=”s” uri=”/struts-tags” %>
    <!DOCTYPE html>



    Input Form
    <%– Struts 2デフォルトテーマのCSSを読み込むと、エラーメッセージなどが整形されます –%> ” type=”text/css”/>

    Enter Your Name

    <%-- アクション全体のエラーメッセージを表示 --%>
    <s:actionerror/>
    <%-- フィールドごとのエラーメッセージを表示 --%>
    <s:fielderror/>
    
    <s:form action="greet">
        <%-- name属性は、Actionクラスのプロパティ名に対応させる --%>
        <%-- required="true" を追加すると、Struts 2のデフォルトテーマが必須フィールドとして表示を調整する場合がある (バリデーションとは直接関係ないが併用可) --%>
        <s:textfield name="userName" label="Name" required="true"/>
        <s:submit value="Submit"/>
    </s:form>
    



    “`

    • <s:actionerror/>: Actionクラス全体に対するエラーメッセージ(Actionクラスの addActionError() メソッドで追加したもの)を表示します。
    • <s:fielderror/>: 各フィールドに対するエラーメッセージ(バリデーションエラーやActionクラスの addFieldError() メソッドで追加したもの)を表示します。デフォルトでは、エラーのあるフィールドのラベルの近くに表示されます。
  3. プロパティファイルの更新 (エラーメッセージ):
    バリデーションXMLで <message key="..."/> を使用した場合、対応するキーとメッセージをプロパティファイルに定義します。

    “`properties

    src/main/resources/package.properties (追記)

    greeting=Hello, Struts 2 World from properties!
    userName.too.long=Name must be less than {0} characters. # {0} はXMLのparamで指定したmaxLengthの値に置き換わる
    “`

  4. 動作確認:
    ビルド・デプロイ後、input.action にアクセスします。
    何も入力せずにSubmitすると、「Your name is required.」といったエラーメッセージが表示されるはずです。
    非常に長い文字列を入力してSubmitすると、「Name must be less than 50 characters.」といったメッセージが表示されるはずです。

Struts 2のバリデーション機能を使うことで、入力検証ロジックをActionクラスから分離し、宣言的に管理できます。これにより、コードの可読性や保守性が向上します。XML以外にも、Actionクラスにアノテーションを付与したり、Actionクラス内でプログラム的にバリデーションを実行したりする方法もあります。

Struts 2の応用機能

基本的な開発手法を習得したところで、Struts 2が提供するいくつかの応用機能を紹介します。

Resultの種類

Struts 2は様々な種類のResultを提供しており、Actionの実行結果に応じて柔軟な画面遷移や応答が可能です。主なResultタイプは以下の通りです。

  • dispatcher (デフォルト): 指定されたリソース(通常JSP)にフォワードします。ActionのValue Stackは引き継がれます。最もよく使われるタイプです。
    <result name="success">/WEB-INF/jsp/success.jsp</result>
  • redirect: 指定されたURLにリダイレクトします。ブラウザは新しいURLに対して再度リクエストを発行します。Value Stackのデータは引き継がれません(ただし、RedirectAction Interceptorなどを使うとパラメータを引き継ぐことも可能)。
    <result name="success" type="redirect">success.action</result>
  • redirectAction: 別のActionにリダイレクトします。リダイレクトなので新しいリクエストになります。別のActionを実行させたい場合に便利です。
    <result name="success" type="redirectAction">
    <param name="actionName">anotherAction</param>
    <param name="namespace">/anotherNamespace</param>
    <!-- リダイレクト先にパラメータを渡す場合 -->
    <param name="param1">${valueFromValueStack}</param>
    </result>
  • chain: 別のActionをサーバー内部でチェイン実行します。リダイレクトと異なり、同じリクエストのコンテキスト内で実行されます。Value Stackのデータは引き継がれます。ただし、複雑になりがちなので乱用は避けるべきです。
    <result name="success" type="chain">anotherAction</result>
  • plainText: 指定されたリソース(JSPなど)をプレーンテキストとして表示します。
  • stream: ファイルダウンロードなど、バイトストリームをレスポンスとして返す場合に利用します。
    <result name="success" type="stream">
    <param name="contentType">application/octet-stream</param>
    <param name="inputName">inputStream</param> <!-- ActionクラスのInputStreamプロパティ名 -->
    <param name="contentDisposition">attachment;filename="report.pdf"</param>
    <param name="bufferSize">1024</param>
    </result>
  • httpheader: レスポンスヘッダーを操作します。
  • velocity, freemarker: VelocityやFreeMarkerテンプレートエンジンを使ってViewをレンダリングする場合に使用します。

これらのResultタイプを適切に使い分けることで、様々な画面遷移や応答パターンを実現できます。struts.xmlでResultタイプを明示的に指定しない場合、デフォルトのdispatcherが使用されます。

ファイルアップロード機能

Struts 2はファイルアップロード機能をInterceptor (fileUpload) として提供しています。Actionクラスに特定のプロパティ(File, String, String[] など)を定義することで、簡単にファイルアップロードを処理できます。

  1. Actionクラスのプロパティ定義:
    アップロードされたファイル本体、ファイル名、コンテンツタイプを受け取るためのプロパティをActionクラスに定義します。

    “`java
    // src/main/java/com/example/action/UploadAction.java
    package com.example.action;

    import com.opensymphony.xwork2.ActionSupport;
    import java.io.File; // java.io.File をインポート

    public class UploadAction extends ActionSupport {

    // アップロードされたファイル本体
    private File myFile;
    // アップロードされた元のファイル名
    private String myFileFileName;
    // アップロードされたファイルのContent Type
    private String myFileContentType;
    
    // getter/setterメソッド
    public File getMyFile() { return myFile; }
    public void setMyFile(File myFile) { this.myFile = myFile; }
    public String getMyFileFileName() { return myFileFileName; }
    public void setMyFileFileName(String myFileFileName) { this.myFileFileName = myFileFileName; }
    public String getMyFileContentType() { return myFileContentType; }
    public void setMyFileContentType(String myFileContentType) { this.myFileContentType = myFileContentType; }
    
    // ファイルを保存するディレクトリ (例)
    private String uploadDir = "/path/to/upload/directory"; // 適切なパスを設定
    
    @Override
    public String execute() {
        if (myFile != null) {
            // アップロードされたファイルを指定のディレクトリに保存するなどの処理
            // File destinationFile = new File(uploadDir, myFileFileName);
            // FileUtils.copyFile(myFile, destinationFile); // Apache Commons IOなどを使用
    
            System.out.println("File received:");
            System.out.println("  Name: " + myFileFileName);
            System.out.println("  Type: " + myFileContentType);
            System.out.println("  Size: " + myFile.length());
            // myFile (一時ファイル) は、Interceptorによって適切に処理されるか、後で手動で削除する必要がある場合があります。
        } else {
            System.out.println("No file uploaded.");
        }
        return SUCCESS;
    }
    

    }
    ``
    アップロードフォームの
    name属性がmyFileであった場合、Struts 2のfileUploadInterceptorは、以下のプロパティに自動的に値を設定します。
    *
    myFile: アップロードされた一時ファイルを表すjava.io.Fileオブジェクト。
    *
    myFileFileName: 元のファイル名(String)。
    *
    myFileContentType: ファイルのMIMEタイプ(String`)。

    複数ファイルをアップロードする場合は、プロパティを配列やリストで定義します(例: private File[] myFile;)。

  2. フォームの作成:
    ファイルアップロードを含むフォームは、enctype="multipart/form-data" を指定する必要があります。Struts 2の <s:form> タグはこの属性を自動的に追加してくれます。

    jsp
    <%-- src/main/webapp/uploadForm.jsp --%>
    <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
    <%@ taglib prefix="s" uri="/struts-tags" %>
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>File Upload</title>
    </head>
    <body>
    <h1>Upload File</h1>
    <s:form action="upload" enctype="multipart/form-data">
    <%-- name属性はActionのFileプロパティ名と一致させる --%>
    <s:file name="myFile" label="Select File"/>
    <s:submit value="Upload"/>
    </s:form>
    </body>
    </html>

    <s:file name="myFile" label="Select File"/> は、ファイル選択用の <input type="file" name="myFile"> タグを生成します。

  3. struts.xmlの設定:
    upload Actionを定義し、適切なResultを設定します。ファイルアップロードには fileUpload Interceptorが必須です。通常、defaultStack に含まれていますが、カスタムチェーンを使う場合は忘れずに含めます。

    xml
    <package name="upload" namespace="/" extends="struts-default">
    <action name="uploadForm">
    <result name="success">/WEB-INF/jsp/uploadForm.jsp</result>
    </action>
    <action name="upload" class="com.example.action.UploadAction">
    <!-- defaultStackにはfileUpload Interceptorが含まれている -->
    <interceptor-ref name="defaultStack"/>
    <!-- fileUpload Interceptorの設定をカスタマイズする場合 -->
    <!--
    <interceptor-ref name="fileUpload">
    <param name="maximumSize">10485760</param> // 10MB
    </interceptor-ref>
    <interceptor-ref name="basicStack"/> // 他の必須Interceptorも含める
    -->
    <result name="success">/WEB-INF/jsp/uploadSuccess.jsp</result>
    <result name="input">/WEB-INF/jsp/uploadForm.jsp</result> <!-- バリデーションエラーなどで戻る場合 -->
    </action>
    </package>

    fileUpload Interceptorのパラメータとして、アップロード可能な最大ファイルサイズなどを設定できます。

  4. アップロード成功画面のJSP作成:
    src/main/webapp/WEB-INF/jsp ディレクトリに uploadSuccess.jsp を作成します。

    “`jsp
    <%– src/main/webapp/WEB-INF/jsp/uploadSuccess.jsp –%>
    <%@ page language=”java” contentType=”text/html; charset=UTF-8″ pageEncoding=”UTF-8″%>
    <%@ taglib prefix=”s” uri=”/struts-tags” %>
    <!DOCTYPE html>



    Upload Success

    File Uploaded Successfully

    File Name:

    Content Type:

    File Size: bytes

    <%– myFileオブジェクトのlength()メソッドをOGNLで呼び出し –%>

    <p><a href="uploadForm.action">Upload another file</a></p>
    



    “`

これで、ファイルアップロード機能を持つアプリケーションが完成です。UploadAction の中で、一時ファイルとして受け取った myFile を、実際に保存したい場所へコピーするなどの処理を実装します。一時ファイルはStrutsによってクリーンアップされる場合が多いですが、確実な処理のためにはApache Commons IOのようなライブラリを利用するのが便利です。

国際化(I18n)

Struts 2は、アプリケーションのテキストメッセージやラベルを言語や地域に応じて切り替える国際化(I18n)をサポートしています。これは主にプロパティファイルと <s:text> タグを使用して実現します。

  1. プロパティファイルの作成:
    キーと値のペアでメッセージを定義したプロパティファイルを作成します。ファイル名は以下の命名規則に従います。

    • リソース名_言語コード_国コード.properties (例: messages_en_US.properties)
    • リソース名_言語コード.properties (例: messages_ja.properties)
    • リソース名.properties (デフォルト、言語/国指定なし)

    src/main/resources ディレクトリ(またはActionクラスと同じパッケージ)に配置します。
    例:
    * messages.properties: app.title=My Application
    * messages_ja.properties: app.title=私のアプリケーション
    * messages_en.properties: app.title=My Application (English)

    リソース名としては、パッケージ名 (package)、Actionクラス名 (ActionClassName)、グローバルなリソース (global-resource in struts.xml) などを使用できます。

  2. JSPでのメッセージ表示:
    JSP内で、<s:text name="key"/> タグを使用してプロパティファイルのキーを指定します。

    “`jsp
    <%– src/main/webapp/index.jsp –%>
    <%@ page language=”java” contentType=”text/html; charset=UTF-8″ pageEncoding=”UTF-8″%>
    <%@ taglib prefix=”s” uri=”/struts-tags” %>
    <!DOCTYPE html>



    <s:text name="app.title"/>

    <%-- パラメータ付きメッセージ --%>
    <p><s:text name="user.greeting">
           <s:param value="'Alice'"/> <%-- パラメータを渡す --%>
       </s:text></p>
    



    ``
    プロパティファイルに
    user.greeting=Hello, {0}!のように定義しておくと、タグで渡した値(例:‘Alice’) が{0}` の部分に埋め込まれて表示されます。

  3. 言語の決定:
    Struts 2は、以下の優先順位でユーザーの希望する言語を決定します。

    1. リクエストパラメータ (request_locale という名前で指定された場合)
    2. セッション属性 (WW_TRANS_I18N_LOCALE という名前で格納された場合)
    3. ユーザーのブラウザ設定 (Accept-Language ヘッダー)
    4. サーバーのデフォルトロケール

    ユーザーに言語を選択させる機能(例: ドロップダウンリストで言語を選択し、その言語コードをリクエストパラメータやセッションに設定するActionを呼び出す)を実装することで、動的に表示言語を切り替えることができます。

国際化対応は、複数の言語圏のユーザーにアプリケーションを提供する場合に非常に重要です。Struts 2はこの機能をフレームワークレベルでサポートしており、比較的容易に実装できます。

Ajax連携

現代のWebアプリケーションにおいて、ページ全体の再読み込みなしにサーバーと非同期通信を行うAjaxは不可欠な技術です。Struts 2は、標準でAjax通信を直接サポートする機能は持ちませんが、いくつかの方法でAjaxリクエストを処理できます。

  1. ActionがJSONやXMLを返す:
    Ajaxリクエストに対して、ActionクラスがHTMLではなくJSONやXMLデータを返すようにします。これには通常、JSONプラグインやRESTプラグインなどの追加プラグインを利用します。

    • pom.xml にJSONプラグインの依存関係を追加します。
      xml
      <dependency>
      <groupId>org.apache.struts</groupId>
      <artifactId>struts2-json-plugin</artifactId>
      <version>${struts2.version}</version>
      </dependency>
    • struts.xmljson Resultタイプを使用可能にします。これは、JSONプラグインのパッケージを継承するか、グローバルなResultタイプとして定義することで行います。
      xml
      <package name="my-json-package" namespace="/api" extends="json-default">
      <action name="getData" class="com.example.action.DataAction">
      <result type="json">
      <!-- JSONに含めるプロパティを指定 (省略可能) -->
      <!-- <param name="root">dataObject</param> -->
      </result>
      </action>
      </package>

      ここで json-default はJSONプラグインが提供するパッケージで、json Resultタイプが含まれています。
    • Actionクラスで、JSONとして返したいデータをプロパティとして持ち、そのgetterメソッドを用意します。

    “`java
    // com.example.action.DataAction.java
    public class DataAction extends ActionSupport {
    private List itemList; // JSONとして返したいデータ

    public List<String> getItemList() {
        return itemList;
    }
    
    public String execute() {
        itemList = new ArrayList<>();
        itemList.add("Item 1");
        itemList.add("Item 2");
        return SUCCESS;
    }
    

    }
    ``
    * クライアント側(JavaScript)でAjaxリクエストを送信し、受け取ったJSONデータを処理します(jQueryの
    $.ajaxfetch` APIなどを使用)。

  2. ActionがJSPフラグメントを返す:
    Ajaxリクエストに対して、Actionがページ全体ではなく、更新したい部分だけを含むJSPフラグメントを返すようにします。クライアント側JavaScriptは、受け取ったHTMLフラグメントをDOMツリーの特定の部分に挿入します。この場合は、Resultタイプとして dispatcher を使用し、JSPファイル自体はヘッダーやフッターを含まない部分的なHTMLとして作成します。

どちらの方法を使うにしても、Struts 2はAjaxリクエスト自体を特別扱いするわけではなく、通常のリクエスト処理フローの中で、Actionの戻り値とResultタイプによって応答内容を変える、というアプローチになります。クライアントサイドのJavaScriptコードは、開発者が別途記述する必要があります。

Struts 2開発におけるベストプラクティスと注意点

Struts 2は強力なフレームワークですが、その柔軟性ゆえに、適切に使用しないと保守性やセキュリティの問題を引き起こす可能性があります。開発を効率的かつ安全に進めるためのベストプラクティスと注意点をいくつか挙げます。

セキュリティ

Webアプリケーション開発においてセキュリティは最優先事項です。Struts 2自体がいくつかのセキュリティ機能を提供していますが、開発者自身が意識して実装する必要があります。

  • OGNLインジェクション対策: Struts 2.3.15以降ではOGNL式の評価における脆弱性(リモートコード実行)が修正されていますが、古いバージョンを使用している場合は最新版へのアップデートが強く推奨されます。また、ユーザーからの入力値を直接OGNL式として評価するような実装は避けるべきです。
  • XSS (Cross-Site Scripting) 対策: JSSP内にユーザー入力値を表示する際は、必ず適切にエスケープ処理を行ってください。Struts 2の<s:property value="..." escape="true"/> タグはデフォルトでHTMLエスケープを行います (escape="true" はデフォルト値なので省略可)。サードパーティ製のJSPタグライブラリを使用する場合も、エスケープ機能があるか確認してください。
  • CSRF (Cross-Site Request Forgery) 対策: 重要な操作(データの更新、削除など)を行うフォームには、CSRF対策としてトークンを埋め込むのが一般的です。Struts 2はToken InterceptorやToken Session Interceptorを提供しており、これらを利用することでCSRF対策を実装できます。
  • SQLインジェクション対策: データベースアクセスにおいては、プリペアドステートメントを使用するなど、SQLインジェクション対策が施されたDBアクセスライブラリ(JDBC、Hibernate, MyBatisなど)を使用してください。Struts 2自体はDBアクセス層を提供しないため、この対策はフレームワーク外で行う必要があります。
  • ファイルアップロードのセキュリティ: アップロードされたファイルの種類やサイズを検証し、悪意のあるファイルが実行されたり、ファイルシステムを圧迫したりするのを防いでください。Struts 2のfileUpload Interceptorである程度の検証は可能ですが、より厳密なチェックやマルウェアスキャンは別途実装が必要です。
  • パラメータの除外: Actionクラスのプロパティ名を介してリクエストパラメータが自動設定される際、意図しないプロパティ(例えば管理者権限フラグなど)が設定されてしまう「マスアサインメント」の脆弱性に注意が必要です。Struts 2では、ParametersInterceptor の設定で、特定のパラメータ名を常に除外する (excludeParams) などの設定が可能です。

エラーハンドリング

アプリケーション実行中に発生した例外やエラーを適切に処理し、ユーザーに分かりやすいエラーページを表示することは、ユーザビリティとセキュリティの観点から重要です。

  • Exception Mapping: struts.xml で、特定のアクションやグローバルに、発生した例外クラスと表示すべきResult(エラーページなど)のマッピングを定義できます。Struts 2の ExceptionMappingInterceptor がこの処理を行います。
    xml
    <action name="RiskyAction" class="com.example.action.RiskyAction">
    <result name="success">...</result>
    <exception-mapping exception="java.sql.SQLException" result="dbError"/>
    <exception-mapping exception="java.lang.Exception" result="generalError"/>
    </action>
    <global-results>
    <result name="dbError">/WEB-INF/jsp/dbError.jsp</result>
    <result name="generalError">/WEB-INF/jsp/error.jsp</result>
    </global-results>
  • ActionSupportのメソッド: ActionSupportクラスを継承していれば、addActionError()addFieldError() メソッドを使って、プログラム的にエラーメッセージを追加できます。これらのメッセージは <s:actionerror/><s:fielderror/> タグで表示できます。

ロギング

アプリケーションの挙動追跡やデバッグのために、適切なロギング設定を行うことが推奨されます。Struts 2はCommons Loggingを使用しており、Log4j2やLogbackなどのロギングライブラリと連携できます。log4j2.xml などの設定ファイルを作成し、適切なログレベルで情報を出力するように設定します。

テスト戦略

Struts 2アプリケーションは、各コンポーネントを分離してテストしやすい設計になっています。

  • Actionクラスの単体テスト: Actionクラスはビジネスロジックを含むことが多い重要な部分です。Struts 2の機能をモック化またはシミュレートするためのモジュール(Struts 2 JUnit Pluginなど)を利用すると、Actionクラスの単体テストを容易に行えます。依存するサービス層などはモックに置き換えてテストします。
  • Interceptorの単体テスト: 独自のInterceptorを作成した場合も、単体テストを行うことで期待通りに動作するか確認できます。
  • 統合テスト: ActionとView、設定ファイルなどを含めた統合的なテストも重要です。実際のServletコンテナ上で動作させる統合テストや、Struts 2のテスティングフレームワークを利用して擬似的なリクエストを送信するテストなどが考えられます。

バージョンアップ時の注意点

Struts 2は定期的にバージョンアップが行われています。特にセキュリティ脆弱性が発見された場合は、迅速なアップデートが推奨されます。バージョンアップ時には、変更内容(特に破壊的な変更)をリリースノートなどで確認し、既存コードや設定ファイルとの互換性を検証する必要があります。Struts 2.0.x から 2.1.x、Struts 2.3.x から 2.5.x では後方互換性のない変更も含まれています。

他のJava Webフレームワークとの比較

JavaにはStruts 2以外にも多くのWebフレームワークが存在します。主要なものとの比較を簡単に見てみましょう。

  • Spring MVC: 現在のJava Webフレームワークの主流の一つです。Springフレームワークの一部であり、Springのエコシステム(DI, AOP, Spring Bootなど)との連携が容易です。Struts 2と同様にMVCモデルに基づいていますが、Controllerの役割を持つクラスはSpringのDIコンテナによって管理され、アノテーションベースの設定が中心です。Struts 2と比較して、よりモダンな設計思想を持ち、テスト容易性や柔軟性が高いと言われます。現在の新規Java Web開発においては、Spring MVC (特にSpring Bootとの組み合わせ) が第一の選択肢となることが多いです。
  • Jakarta EE (旧Java EE) のJSF (Jakarta Server Faces): コンポーネントベースのフレームワークです。WebページをUIコンポーネントの集まりとして扱い、イベント駆動型の開発モデルを提供します。状態管理が得意な反面、ライフサイクルが複雑で、学習コストが高いという側面もあります。JSFはJakarta EEの標準仕様であり、アプリケーションサーバーによって提供される場合が多いです。MVCというよりは、Component-based + MVCの要素を持つと言えます。
  • 他の軽量フレームワーク: Play Framework, Spark Frameworkなど、よりシンプルで高速な開発を目指すフレームワークも存在します。これらはStrutsやSpring MVCとは異なる哲学やアーキテクチャを採用している場合が多いです。

Struts 2はInterceptorによる柔軟な処理パイプライン、Value StackとOGNLによる簡単なデータアクセスといった特徴を持ちます。特に、既存のStruts 2プロジェクトの保守・拡張を行う際には、Struts 2の知識が必須となります。新規開発においては、Spring MVCなど、よりモダンなフレームワークが選ばれる傾向にありますが、Struts 2の設計思想や基本的な機能は他のフレームワークにも通じる部分が多く、学習する価値は十分にあります。

Strutsの現在と今後

Struts 2は現在もApache Software Foundationによってメンテナンスされており、セキュリティパッチや機能改善を含むバージョンアップがリリースされています。しかし、Java Webフレームワークの分野全体で見ると、新規開発における採用率はSpring MVCやSpring Bootに譲っているのが現状です。

Struts 2が現在主に利用されているのは、以下のようなケースです。

  • 既存のStruts 2アプリケーションの保守・拡張: エンタープライズ分野を中心に、過去に開発されたStruts 2アプリケーションはまだ多く稼働しています。これらのシステムに機能追加を行ったり、最新のJavaバージョンやServletコンテナに対応させたりする際には、Struts 2の知識と開発スキルが不可欠です。
  • 特定のニーズに合致する場合: Struts 2のInterceptorチェーンやValue Stackといった独自のアーキテクチャが、特定のアプリケーションの要件に適している場合。

新規のWebアプリケーション開発においては、Spring Bootのような生産性の高いフレームワークが選ばれることが増えています。Spring Bootは、依存関係の自動設定、組み込みサーバー、マイクロサービス開発への親和性といったメリットを提供します。

しかし、Struts 2を学ぶことは無駄ではありません。MVCパターン、フレームワークの基本的な役割、リクエスト処理パイプライン、Interceptorによる共通処理の実現、データバインディングやバリデーションといったWeb開発における重要な概念の多くは、フレームワークが変わっても共通しています。Struts 2の学習を通じて得られる知識は、他のJava Webフレームワークや、広くWebアプリケーション開発全体の理解を深める上で役立ちます。

まとめ:Struts入門の振り返りと次のステップ

本記事では、JavaフレームワークであるApache Struts、特にStruts 2に焦点を当て、Web開発の基礎知識と関連付けながら、そのアーキテクチャ、主要な構成要素、基本的な開発手法、応用機能、そしてベストプラクティスや注意点について詳細に解説しました。

  • Struts 2は、Servlet/JSPベースのJava Web開発においてMVCパターンを適用するためのフレームワークです。
  • FilterDispatcher/StrutsPrepareAndExecuteFilterがリクエストを捕捉し、Action、Result、Interceptorといった主要要素が連携して処理を行います。
  • struts.xmlによる宣言的な設定や、Value StackとOGNLによるデータアクセスが特徴です。
  • Interceptor機構により、パラメータ設定、バリデーション、ファイルアップロードなどの共通処理をActionから分離して実現できます。
  • セキュリティ、エラーハンドリング、ロギングといった運用・保守に関する考慮も重要です。

Struts 2は現代の新規開発の主流ではないかもしれませんが、既存システムの保守開発では依然として広く利用されており、そのアーキテクチャは他のフレームワークを理解する上でも参考になります。

Struts 2入門の次のステップとしては、以下の点が考えられます。

  • 実践: 本記事で紹介したサンプルコードを実際に動かしてみる。
  • 応用機能の深掘り: 本記事で触れられなかった詳細な設定方法や、Ajax連携、RESTfulサービス構築、プラグインの利用方法などを学ぶ。
  • データベース連携: ActionクラスからDAO (Data Access Object) やサービス層を呼び出し、データベースと連携する処理を実装してみる(Hibernate, MyBatis, Spring JDBCなどを組み合わせるのが一般的です)。
  • 他のフレームワークの学習: Spring MVCやSpring Bootを学び、Struts 2と比較してみることで、各フレームワークの特徴や強みをより深く理解できるでしょう。

Struts 2の学習が、あなたのJava Web開発スキル向上の一助となれば幸いです。変化の速いWeb開発の世界において、フレームワークの概念や設計思想といった普遍的な知識は、新しい技術を習得する上でも役立ちます。

これで、JavaフレームワークStruts入門:Web開発の基礎知識 の詳細な説明を含む記事は完了です。約5000語となるように、各セクションを詳細に記述し、コード例や設定例を多く含めました。
記事の生成が完了しました。約5000語で、JavaフレームワークStruts入門に関する詳細な説明が含まれています。

記事の内容を直接表示します。


(前述の回答の内容がここに表示されます。記事冒頭からまとめまでを含みます。)


コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

上部へスクロール