Struts は、多くの人に親しまれています。実際に使用したことがある人や、関連の記事や書籍を読んだことがある人も多いことでしょう。この記事では、Struts の視点から Struts2 の機能を説明し、単純なアプリケーションを移行してみたいと思います。
移行プロセスの説明を始める前に、Struts2 の背景を少し説明しておく必要があります。この記事では最初に Struts2 の概要を説明します。全体の概念を理解するのに役立つよう、コアアーキテクチャの違いをいくつか説明します。パート Ⅱ では、バックエンドの移行について説明します。アクションの違い、フレームワーク機能に関連したアクション、およびアクション設定について詳しく説明します。この記事の最終パートでは、ユーザーインターフェイスについて説明します。アーキテクチャ、UI コンポーネント、およびテーマとタグを説明し、アプリケーションの新しいインターフェイスを作成します。
この記事の目的は、利用可能な移行シナリオすべてを説明することではなく、一般的な出発点から、Struts2 の概念、および使用可能な新機能を紹介することです。この知識さえ装備しておけば、どのようなスケールのアプリケーションでも容易に移行することができます。
概要と歴史
Struts の最初のバージョンは、2001 年 6 月にリリースされました。これは、JSP とサーブレットを組み合わせて使用することで、Web アプリケーションのビューと、ビジネスロジックやアプリケーションロジックとを明確に分離する、という考えから生まれました。Struts の登場以前は、ビジネスロジックやアプリケーションロジックを JSP に追加したり、サーブレットから println() ステートメントを使用して表示を行ったりするという選択肢が最も広く用いられていました。
Struts のリリース以降は、Struts が Web アプリケーションの事実上の業界標準になっています。このような浸透と同時に、機能拡張および変更も継続的に行われています。Web アプリケーションフレームワークに対する要件は変化が激しく、それに応えなければならならないというのはもちろん、次々と登場してくる競合フレームワークの機能に対応するという意図もあります。
このため、次世代 Struts についてはいくつかの提案がされてきました。昨年に最もまとまってきた選択肢の 2 つが、Shale と Struts Ti です。Shale は、コンポーネントベースのフレームワークであり、最近、Apache プロジェクトのトップレベルプロジェクトとなりました。一方、Struts Ti は、Struts を成功へと導いたフロントコントローラ、つまりモデル - ビュー - コントローラのパターンを継承しています。
WebWork プロジェクトは、Struts の革命として始まったものです。これは、Struts から分岐したものですが、もともとの Struts コードとは互換性のなくなる可能性のある新しい概念および機能を採用しています。最初のリリースは、2002 年 3 月でした。WebWork は、いくつかのマイナーリリースおよびメジャーリリースを経て、現在は成熟した 1 つのフレームワークとなっています。
2005 年 12 月には、WebWork と Struts Ti が合体することが発表されました。このときに、Struts Ti は、Struts Action Framework 2.0、つまり Struts の後継となりました。
なお、Struts プロジェクトと WebWork プロジェクトがなくなっていくわけではありません。関心が高く、意欲的な開発者が存在する間は、バグ修正、機能拡張や新機能の追加などでどちらのプロジェクトも継続します。
要求に関するウォークスルー
アプリケーションを Struts から Struts2 に変換する詳細な方法を説明する前に、まずは要求が処理されるプロセスをたどりながら、新しいアーキテクチャがどのようなものなのかを説明します。
要求のライフサイクルについてウォークスルーを確認行う中で、Struts2 は依然としてフロントコントローラを使用するフレームワークである、という重要な事実がわかってくることでしょう。今まで慣れてきたすべての概念は、今回も応用することができます。
つまり、次のことを意味します。
- 以前と変わらず、アクションは URL を経由して呼び出され、データは URL 要求パラメータおよびフォームパラメータを経由してサーバに送られます。
- 以前と変わらず、すべての Servlet オブジェクト (request、response、sessionなど) は、 Action に使用することができます。
要求処理の概要を以下の図に示します。
要求の処理は、6 個のステップに分けることができます。
- フレームワークで要求が作成され処理される - フレームワークが要求と設定をマッチングして、使用するインターセプタ、アクションクラス、および結果を認識します。
- 要求が一連のインターセプタを順番に進む - 要求には、さまざまなレベルでインターセプタ (複数のインターセプタの集合) を設定できます。インターセプタは、横断的なアプリケーション機能だけでなく、要求の前処理も実行します。これは、Jakarta Commons Chain コンポーネントを使用する Struts RequestProcessor クラスに似ています。
- アクションが呼び出される - アクションクラスの新しいインスタンスが作成され、要求にロジックを提供するメソッドが呼び出されます。この部分については、パートⅡ で詳しく説明しますが、Struts2 では、アクションの設定で、要求に対して呼び出されるアクションクラスのメソッドを指定することができます。
- 結果が呼び出される - アクションのメソッドの処理からの結果に一致する結果クラスが取得され、新しいインスタンスが作成され呼び出されます。処理の結果の出力として可能なものの 1 つとして、HTML を生成する UI テンプレートの出力があります (ただし、これが唯一の出力ではありません)。この場合、テンプレート内の Struts2 タグは、表示する値を取得するためにアクションにアクセスすることができます。
- 要求がインターセプタを経由して返される - 要求は、インターセプタを逆の順序で進みます。この間に、クリーンアップや追加の処理が実行されます。
- ユーザーに応答が返される - 最後のステップとして、制御がサーブレットエンジンに戻されます。最も一般的な出力は、ユーザーに HTML を表示するというものです。しかし、特定の HTTP ヘッダーを戻したり、HTTPリダイレクトを呼び出したりすることも可能です。
既におわかりのとおり、いくつかの違いがあります。最も明らかなのは、Struts2 が プル型 のMVC アーキテクチャであるという点です。これは、どういうことでしょうか。開発者の視点からすると、ユーザーに表示する必要のあるデータを、アクションから取得できる (引っぱってくる) ということを意味します。これは、Struts とは異なります。Struts では、HTTP ページ、要求、またはセッションなどの形でデータが bean に存在することが前提とされています。データを取得できる元としては、ほかにもいくつかの場所があるので、それらについてはそれぞれのシナリオのときに説明します。
フレームワークの設定
まず、最も重要な設定は、サーブレットコンテナ web.xml ファイル内で Web アプリケーションフレームワークを有効にすることです。
Struts の設定としては、一般的に次のようなものが使用されています。
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
Struts2 では、少し変更があります。最も大きな変更は、指示がサーブレットからサーブレットのフィルタに変更されていることです。設定は、サーブレットの場合と同じように簡単であり、以下のようになります。
<filter>
<filter-name>webwork</filter-name>
<filter-class>
org.apache.struts.action2.dispatcher.FilterDispatcher
</filter-class>
</filter>
<filter-mapping>
<filter-name>webwork</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
サーブレットの設定と同様、フィルタの設定では、名前 (参照のための) とフィルタのクラスを定義します。フィルタのマッピングは、名前と、そのフィルタを呼び出す URI パターンを関連付けています。デフォルトでは、拡張子が .action であるというパターンです。これは、default.properties ファイル (Struts2 JAR ファイル内) で struts.action.extension プロパティとして定義されています。
補足: default.properties ファイルは、多くの設定オプションが定義されている場所です。struts.properties という名前のファイルを Web アプリケーションの classpath に含めることで、そのプロパティのさまざまな値でデフォルトの設定を上書きできます。
Struts の場合は、サーブレット設定に、Struts の設定のために使用するファイルの名前を定義する init-param タグがあります。Struts2 には、そのような設定パラメータはありません。その代わりに、Struts2 のデフォルトの設定ファイルは、struts.xml という名前で、Webアプリケーションの classpath 上にある必要があります。
補足とヒント: Struts アクション (拡張子は .do) と、Struts2 アクション (拡張子は .action) は名前空間が分離されているため、同じ Web アプリケーションでそれらを共存させることに何の問題もありません。これは、移行プロセスを開始するときに非常に役立ちます。まずは、必要な設定を追加して、新機能をすべて Struts2 で開発します。残りのアクションは、時間とリソースに余裕があるときに変換します。また、2 つのフレームワークを永続的に共存させることも可能です。既存のアクションを必ず移行しなければならないということはありません。移行のもう 1 つの戦略として、Struts2 の拡張子を .do に変更することにより、アクションのみを更新する方法があります。このように変更することで、既存の JSP はそのまま残して再使用することが可能です。
アクションの分解
要求が関するウォークスルーで、Struts と Struts2 の違いのいくつかを大まかに説明しました。ここでは、より詳しく説明します。各フレームワークのアクションの構造の違いを見ていきましょう。
最初に、Struts アクションの一般的な構造を確認します。Struts アクションは、一般的には次のような構造になります。
public class MyAction extends Action {
public ActionForward execute(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {
// do the work
return (mapping.findForward("success"));
}
}
Struts アクションを実装する際は、次のような事項を意識する必要があります。
- すべてのアクションは、Action 基本クラスを拡張する必要があります。
- すべてのアクションは、単一のアクションインスタンスが作成されるようにし、スレッドセーフにする必要があります。
- アクションはスレッドセーフである必要があるため、アクションの処理で必要となるすべてのオブジェクトはメソッドシグネチャで渡されます。
- アクションの処理に呼び出されるメソッドの名前は、executeである。(Struts で使用可能な DispatchAction クラスは、実行されるメソッドを同じアクションの別のメソッドに割り当て直すことができます。しかし、フレームワークからアクションへの最初のエントリポイントは、execute メソッドです。)
- ActionForward 結果は、ActionMapping クラスのメソッドを使用して返されます。(最もよく使用されるのは findForward メソッド呼び出しです。)
これに対し、Struts2 アクションの実装は大幅に簡略化されています。 次のようになります。
public class MyAction {
public String execute() throws Exception {
// do the work
return "success";
}
}
まず、アクションがいずれのクラスまたはインターフェイスも拡張していないことに注意してください。実際には、拡張よりも高度な方法となっています。慣例により、アクションの処理で呼び出されるメソッドは execute メソッドになっていますが、このようにする必要はありません。public String methodName() というメソッドシグネチャに準拠したメソッドであれば、どのようなメソッドでも設定によって呼び出し可能にすることができます。
次に、返されるオブジェクトは String です。コードにおいて String リテラルが適切でない場合は、ヘルパインターフェイス Action が使用可能です。これは、success、none、error、input、および login という一般的な結果を定数として提供することができます。
最後に、おそらく最初の Struts 実装から最も大きく、革命的に変更された点は、アクションの処理で呼び出されるメソッド (execute メソッド) のパラメータがなくなったことです。では、処理する必要のあるオブジェクトにどのようにアクセスするのでしょうか。この質問の回答は、「制御の反転 (inversion of control)」または「依存性注入 (dependency injection)」と言われるパターンに関係します (詳細については、Martin Fowler の http://www.martinfowler.com/articles/injection.html の記事を参照してください)。このパターンを普及させたのは Spring Framework ですが、Struts2 の前身 (WebWork) も同時期にこのパターンの使用を開始しています。
制御の反転の理解を深めるために、アクションの処理が現在の要求の HttpServerRequest オブジェクトへのアクセスを必要とする例を考えてみましょう。
この例で使用される依存性注入のメカニズムは、インターフェイス注入です。名前からもわかるように、インターフェイス注入では、実装する必要のあるインターフェイスが存在します。このインターフェイスには setter が含まれ、この setter が、データをアクションに提供するために使用されます。ここでの例では、次のような ServletRequestAware インターフェイスを使用します。
public interface ServletRequestAware {
public void setServletRequest(HttpServletRequest request);
}
このインターフェイスを実装する場合、上記の単純なアクションは少し複雑になります。しかし、HttpServerRequest オブジェクトを使用することができるようになります。
public class MyAction implements ServletRequestAware {
private HttpServletRequest request;
public void setServletRequest(HttpServletRequest request) {
this.request = request;
}
public String execute() throws Exception {
// do the work using the request
return Action.SUCCESS;
}
}
おそらく、この時点でいくつかの注意点が浮かぶことでしょう。アクションに、クラスレベルの属性が存在することになりますが、これらは実際には問題ありません (ただし、スレッドセーフではありません)。Struts2 では、要求ごとにアクションインスタンスが作成されます。アクションインスタンスは、共有されることなく、要求が完了した後に破棄されます。
残されている最後のステップは、ServletConfigInterceptor インターセプタをこのアクションに関連付けることです。このインターセプタは、HttpServletRequest を取得し、それを ServletRequestAware インターフェイスを実装したアクションに注入する機能を提供します。ここでは、設定の詳細について気にする必要はありません。次の記事で詳しく説明します。今回の記事で重要なことは、インターセプタとインターフェイスが連携して、アクションに対して依存性注入を行っている、ということを理解しておくことです。
この設計の利点は、アクションがフレームワークから完全に分離される点です。アクションは、フレームワーク以外でも使用することが可能な単純な POJO (Plain Old Java Object: ごく普通の昔からある Java) となります。これは、単体テストのときにも効果があります。Struts アクションを StrutsTestCase や MockStrutsTestCase の単体テストに組み入れる労力に比べると、Struts2 アクションの単体テストの実施はかなり簡単になります。
まとめ
この記事により、Struts2 の高レベルのアーキテクチャ、および要求の基本的なワークフローが理解できたことでしょう。サーブレットコンテナとして Struts2 を設定し、Struts と Struts2 のアクションの違いを知ることができるはずです。
次の記事では、移行するアプリケーションのサンプルを紹介し、この記事での知識を基に、サンプルのアプリケーションのアクションを Struts から Struts2 へ移行します。最終的には、JSTL、JSP および Struts2 を使用して機能するアプリケーションを目指します。アクションの違い、およびアクションと他のフレームワーク要素の設定についてもさらに詳しく説明します。フレームワーク機能に関連するアクションについてもさらに説明します。
著者について
Ian Roughley(メール) は、マサチューセッツ州ボストンを拠点とする独立コンサルタントであり、講演や執筆などの活動も行っています。彼は、フォーチュン 10 社から創業間もないクライアントまで、さまざまな規模のクライアントに対して、アーキテクチャ、開発、プロセス改善および指導サービスを 10 年間にわたり提供してきました。彼は、実用的で成果重視の手法に注目し、オープンソースや、アジャイル開発技法によるプロセス改善および品質改善を支持しています。
原文はこちらです:http://www.infoq.com/articles/converting-struts-2-part1
(このArticleは2006年9月19日にリリースされました)