2007-06-10

すぐに始める Struts2 (Mayaa編)

Eclipseを入れて、Subversionからソースをチェックアウトして、アプリケーションサーバを Runして、動作を見ながら JSPを書き換えてくれる HTMLライターさんなんて数えるほどしかいないのだよ、世の中には!

というわけで Struts2の画面表示をするにあたって JSPのかわりに Mayaa を使う方法を調べた。
Mayaaを私の理解している範囲で簡単に説明すると、IEや Firefoxでそのまま表示できてしまうようなごく普通の HTMLファイルが不思議と動的なページの表示に使えてしまうテンプレートエンジン(動的要素の定義はHTMLファイルの隣に mayaaファイルとして配置する)ということらしい。これだと HTMLライターとプログラマの協業が殴り合いも発生せずスムーズにいく(かもしれない)というわけ。
とはいえ Mayaaについてそれほど深く知らないので、ここではStruts2との統合についてだけ取り扱う。実際に思惑通りの使い方が出来るかどうかはこれから試してみる。

Struts2 Mayaa Plugun

このプラグインのサンプルには当然 struts.xmlでの設定方法が含まれているが、このブログでは struts.xmlなしでどこまで行けるかチャレンジすることになっているので(そうなの?)アノテーションだけで Mayaaを呼び出すことにした。

WEB-INF/lib に追加で入れたものは下記の通り。

commons-beanutils-core-1.7.0.jar
commons-collections-3.1.jar
jaxen-1.1.jar
js-1.6R5.jar
mayaa-1.1.10.jar
nekohtml-0.9.5.jar
struts2-mayaa-plugin-1.0.0.jar
xercesImpl-2.7.1.jar

web.xmlには下記の設定を追加。

<servlet>
<servlet-name>MayaaServlet</servlet-name>
<servlet-class>org.seasar.mayaa.impl.MayaaServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

MayaaServletを呼び出すことは無いのだが、load-on-startupでこれを自動ロードしておかないといくつかの内部コンポーネントが初期化されないという問題があるため設定を追加する必要がある。この servletに対する servlet-mappingは不要。

アクションクラスに対する Resultアノテーションは下記のようになる。Resultの typeが MayaaResult.classになり、valueが HTMLファイルを指す。

@Results({
@Result(name="success",type=MayaaResult.class,value="success.html"),
})

上記の例ではアクションが successを返したときに success.htmlと success.mayaa が処理される形になる。
HTMLファイルとmayaaファイルを表側から直接見られないように WEB-INFの下へ配置してみたところ、org.seasar.mayaa.impl.source.ForbiddenPathException: Access Forbidden to /WEB-INF/.... などと言われて読み込めなかった。これは何か設定で回避できるのかもしれないがまだ調べていない。というかGoogle Code Searchで Mayaaのソースが見つからなかった。(追記:WEB-INF/page 以下に HTMLファイルを置けることが判明、このパスは自動的に検索されるので、例えばWEB-INF/page/hoge.htmlを使用したい場合単に hoge.htmlと指定すれば良い )

ラベル:

すぐに始めるStruts2(フォームとバリデーション編)


相変わらず struts.xml無しでどこまで出来るかを検証中。
「できないこと」も色々(というか、かなり)あることがわかりつつあるが、ひとまずできるところまで紹介したい。
なお無設定運用の Struts2では、アクション名とアクションメソッド名の区切りを URL上で ! 文字を用いることで表現する(デフォルトのアクションメソッド名は execute)。
アクションクラスEmailActionの場合は、ブラウザから email!input.action がリクエストされると input() がコールされる。
単に email.action がリクエストされた場合は execute() がコールされる。

EmailAction.java

import org.apache.struts2.config.Result;
import org.apache.struts2.config.Results;

import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.validator.annotations.EmailValidator;
import com.opensymphony.xwork2.validator.annotations.RequiredStringValidator;
import com.opensymphony.xwork2.validator.annotations.ValidatorType;

@Results({
@Result(name="input",value="input.jsp"),
@Result(name="success",value="success.jsp")
})

public class EmailAction extends ActionSupport {

private static final long serialVersionUID = 1L;

private String email;

/**
* @return the email
*/

public String getEmail() {
return email;
}

/**
* @param email the email to set
*/

@EmailValidator(type = ValidatorType.FIELD,
message="メールアドレスが正しくない様です。")
@RequiredStringValidator(type = ValidatorType.FIELD,
message="メールアドレスを入力して下さい。")
public void setEmail(String email) {
this.email = email;
}

public String input()
{
// フォームの初期値設定などをここでする。不要な場合はこのメソッド自体省略しても良い
// (ActionSupport#input()で { return "input" } が実装されているから)
return "input";
}

public String execute()
{
// バリデーションが通過したのちにこのメソッドがコールされる。
// ここでユーザの入力内容をデータベースに保存するなどの処理をする

return "success";
}

}

input.jsp

<%@page contentType="text/html;charset=UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<s:head/>
</head>
<body>
<h1>アドレスを追加する</h1>

<s:actionerror />
<s:form>
<%-- s:textfieldのようなUIタグを使うとバリデーションエラーが自動で表示される --%>
<s:textfield label="メールアドレス" name="email"/>
<s:submit />
</s:form>

</body>
</html>

フォームに表示すべきエラーにはアクションエラーとフィールドエラーがある。
フィールドエラー(メールアドレスが正しくありません、など)は UIコンポーネントと
共に自動的に表示されるが、アクションエラー(特定のフィールドに属さないエラー)は
明示的に <s:actionerror/>タグで表示場所を指定する必要がある。もしあればだが。

参考リンク

Apache Struts 2 Documentationより
Annotations
Struts Tags
Class ActionSupport

サンプルアプリケーションの中に収録されているなかなか役に立つ文書
A Walking Tour of the Struts 2 MailReader Application

ラベル:

ちょっと便利なOGNL式

JSPを書くときに OGNL式が便利だと思った例。

JSTL+EL式の場合

性別:
<c:if test="${sex==1}">男性</c:if>
<c:if test="${sex==2}">女性</c:if>
<c:if test="${sex==3}">その他</c:if>

Struts2 Taglib+OGNL式の場合

性別:
<s:property value="#{1:'男性',2:'女性',3:'その他'}[sex]"/>


OGNLでは#{...} 式を使うことによってその場で Mapオブジェクトを生成出来るので、上記のように定数に対応するラベルを表示したい(しかも内容的にマスタを持って管理するほどでもない)場合に記述が短くできる。

ラベル:

2007-06-05

すぐに始める Struts2 (フォームでの日付入力編)


Struts2の Taglibには JavaScript+CSSで出来たリッチなUIコンポーネントが収録されている(DoJo Toolkitなるライブラリを使っているらしい)。今回は s:datetimepicker タグを使い、リッチなUIで日付の入力が可能なフォームを作ってみる。通常だと、フォームからの日付入力を受け取るには文字列型で入力→アクションクラス側でパースしてDate型変数に代入という手順を踏むところだが、この例では直接 java.util.Date型のプロパティにユーザの入力した日付がセットされる。

アクションクラス

この例では、フォームの表示と submitに一つのアクションクラスを使い回している

package yourpackage;

@Results({
@Result(name="success",value="dateinput.jsp")
}
)
public class DateInputAction {
/**
* Date型プロパティ。デフォルトでは newしたDateオブジェクト(現在時刻をさす)
* をセットする。
*/

private java.util.Date date = new java.util.Date();

/**
* この例では、日付入力ボックスの初期値表示時に呼び出される
*/

public java.util.Date getDate() {
return date;
}

/**
* フォームのsubmit時、日付入力ボックスの内容がここに渡される
*/

public void setDate(java.util.Date date) {
this.date = date;
}

public String execute()
{
return "success";
}
}

フォームJSP

<%@page contentType="text/html;charset=UTF-8"%>
<%@taglib prefix="s" uri="/struts-tags"%>
<html>
<head>
<s:head/>
</head>
<body>
<h1>日付入力のデモ</h1>
<s:form>
<s:datetimepicker
label=
"日付を選択してください"
displayFormat=
"yyyy/MM/dd"
name=
"date"/>
<s:submit />
</s:form>
</body>
</html>

解説

Struts2の組み込まれたWebアプリケーションのコンテキストでは、/struts 以下の URIに CSSや DoJoのようなJavaScriptといったリソースが自動的に配備されている。例えばコンテキスト名が myapp の場合、URL的には http://yourhost:port/myapp/struts/ 以下がそれにあたる。
HTMLの head要素内に <s:head/> というタグを書いておくことで、それらの呼び出しに必要なスクリプト等が挿入される。(言い方を変えると、これを書いておかないと s:datetimepickerのような リッチUIタグが動作しない)

struts1からの流儀だが、フォームUIタグの name属性に指定された名前は HTML要素の name属性として用いられると同時に、アクションクラス(Struts1の場合フォームBean)のプロパティ名に直接対応づけられ初期表示時にその値が用いられる。この例では s:datetimepickerが name="date"属性によりアクションクラスの dateプロパティと関連づけられている。

なお、action属性の省略された s:form要素はこのページを表示したアクション自身に対してsubmitされる。

ラベル:

2007-05-30

すぐに始める Struts2

これは、Struts2を動かす最短の動作をメモしたもの。
スターターパッケージのようなものを使わず、最小限の構成を手で作ることによってStruts2を理解することを目標にしている。
Struts2の設定は CLASSPATH上のstruts.xmlに書くことになっているが、下記の例では無くても一応動作する。
物事の関連がわかりやすいよう、各所に色を付けておいた。

/WEB-INF/libに入れるもの

freemarker-2.3.8.jar
ognl-2.6.11.jar
struts2-core-2.0.6.jar
xwork-2.0.1.jar

/WEB-INF/web.xmlに書くこと

<filter>
<filter-name>struts</filter-name>
<filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
<init-param>
<param-name>actionPackages</param-name>
<param-value>your.action.package.here</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>struts</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

作るクラス

/WEB-INF/classes/your/action/package/here/SimpleAction.class

(ソース)
package your.action.package.here;

import org.apache.struts2.config.Result;
import org.apache.struts2.config.Results;

@Results({
@Result(name="success",value="simple.jsp"),
})
public class SimpleAction {
private String myProperty = "My Property String";
public String getMyProperty()
{
return myProperty;
}
public String execute() throws Exception
{
return "success";
}
}

作るJSPファイル

/simple.jsp

(ソース)
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<body>
<h1>Simple</h1>
<s:property value="myProperty" />
</body>
</html>

コールするURL

http://yourhost:port/appname/simple.action

表示されるHTML

<html>
<body>
<h1>Simple</h1>
My Property String
</body>
</html>

解説

struts2はサーブレットフィルタとして動作する。フィルタのinit-paramに actionPackagesというパラメータをセットすると、そこで指定されたパッケージ以下にある *Action.classを探し出して全てをアクションクラスとして登録してくれる(*の部分がアクション名になる)。これは「設定より規約」のムーブメントを意識した機能だと思われるが、現在の所Experimentalな機能という扱いのようだ。

Struts2のアクションクラスは何らかの基底クラスを継承することを義務づけられていない(が、ActionSupportなどのサポートクラスを継承しても良い)。Struts2は、アクションに対応するURLがリクエストされるとアクションクラスをインスタンス化し、setterメソッドを使用して入力パラメータをセットしたのちに execute()メソッドをコールする。Spring-MVCのControllerクラスは基本的にシングルトンで、パラメータは関連づけられたCommandクラスのオブジェクトによって渡されるのに対し、Struts2ではアクションクラスのオブジェクトがリクエスト毎に Instantiateされるため、いわゆるフォームBeanの役割を兼ねることが出来る。(このあたりのモデルはS2Strutsに近い、どちらが先なのかは知らないが)

execute()メソッドの返す結果文字列に対応するビューの名前は Resultアノテーションを使ってセット出来る。従来なら外部の設定ファイルで別に定義されていたものが、Struts2ではソースコードの中に統合できることになる。アクションクラスのモジュール独立性を考慮するならこれは無条件に賛成を得られる方式ではないが、どうせWebアプリケーションのControllerなど作り捨てで再利用などしようはずもないという現実主義に基づくのであれば有用である。
(誤解のないように言っておくと、ここではControllerとModelを的確に分離するように設計ができていることが前提になっているのであって、コンポーネントの再利用自体を諦めているわけではない)

JSPファイル中では /struts-tags タグライブラリを用いることでコントローラ(アクション)オブジェクトの持つプロパティにアクセスすることが出来る。

ラベル:

2007-04-10

Struts2を動作させるために最低限必要な物

最低限、下記のようなファイル構成が必要だった。

./src/java/struts.xml
./src/webapp
./src/webapp/WEB-INF
./src/webapp/WEB-INF/classes
./src/webapp/WEB-INF/lib
./src/webapp/WEB-INF/lib/freemarker-2.3.8.jar
./src/webapp/WEB-INF/lib/ognl-2.6.11.jar
./src/webapp/WEB-INF/lib/struts2-core-2.0.6.jar
./src/webapp/WEB-INF/lib/xwork-2.0.1.jar
./src/webapp/WEB-INF/web.xml


Spring Frameworkも組み合わせて使うならこれも。

./src/webapp/WEB-INF/lib/spring.jar
./src/webapp/WEB-INF/lib/struts2-spring-plugin-2.0.6.jar
./src/webapp/WEB-INF/applicationContext.xml

web.xmlへの記述はこんなふう。

<filter>
  <ilter-name>struts</filter-name>
  <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
</filter>
<filter-mapping>
  <filter-name>struts</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

Springを組み合わせるならこれもね。

<listener>
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

ラベル: