Seasar Conference 2008 Autumn

Seasar Conference 2008 Autumn - 9/6(SAT), Tokyo

気が付いたら申し込みが開始されていたので、早速申し込みました。

会社からお金は出ないと思うので、仙台から自費で参加します(泣)。

SAStrutsS2JDBCの最新機能」と「SAStrutsの開発Tips」はどちらも聞きたいんですが、
同じ時間なんですよねー。どっちに出ようかなー。

SAStrutsで独自の検証用アノテーションを作成する

SAStrutsで既に@Requiredなどいくつかの検証用アノテーションが利用できます。
そこで、それらとは別に独自の検証用アノテーションを作成しようと思ったのですが、
私の探し方が悪いのかSAStrutsのドキュメントなどには記述が見つかりませんでした。
仕方が無いので、SAStrutsソースコードを追いながら、作成してみたいと思います。

SAStrutsのドキュメント内に記述がありました。以下のリンクの最後のほうを参照です。
id:higayasuo さん、ありがとうございます。
http://sastruts.seasar.org/featureReference.html#Validator

検証用アノテーションクラスを作成する

今回は、郵便番号を検証するアノテーションを作成します。
@Maskを利用すれば実現できますが、まあサンプルということで…

Zip.java
package takehiro.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.seasar.struts.annotation.Arg;
import org.seasar.struts.annotation.Msg;
import org.seasar.struts.annotation.Validator;

/**
 * 郵便番号かどうかを検証するためのアノテーションです。
 * 
 * @author takehiro
 *
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Validator("zip")
public @interface Zip {

    /**
     * メッセージです。
     * 
     */
    Msg msg() default @Msg(key = "errors.zip");

    /**
     * メッセージの最初の引数です。
     * 
     */
    Arg arg0() default @Arg(key = "");

    /**
     * 検証の対象となるメソッド名を指定します。 複数ある場合はカンマで区切ります。
     * 
     */
    String target() default "";
}

@Validator("zip") を指定するのがミソです。引数に指定した "zip" が
このアノテーションを指すキーになります。

検証用メソッドを作成する

次は、実際に郵便番号を検証するメソッドを作成します。
org.seasar.struts.validator.S2FieldChecks を継承して新しいクラスを作成しました。

CustomizedS2FieldChecks.java
package takehiro.validator;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.validator.Field;
import org.apache.commons.validator.GenericValidator;
import org.apache.commons.validator.Validator;
import org.apache.commons.validator.ValidatorAction;
import org.apache.struts.action.ActionMessages;
import org.seasar.struts.validator.S2FieldChecks;

/**
 * Seasar2用の独自検証メソッドです。
 * 
 * @author takehiro
 *
 */
public class CustomizedS2FieldChecks extends S2FieldChecks {

    private static final long serialVersionUID = 1L;

    /**
     * 郵便番号かどうかをチェックします。
     * 
     * @param bean
     *            JavaBeans
     * @param validatorAction
     *            バリデータアクション
     * @param field
     *            フィールド定義
     * @param errors
     *            エラーメッセージの入れ物
     * @param validator
     *            バリデータ
     * @param request
     *            リクエスト
     * @return 検証結果がOKかどうか
     */
    public static boolean validateZip(Object bean,
            ValidatorAction validatorAction, Field field,
            ActionMessages errors, Validator validator,
            HttpServletRequest request) {
        String value = getValueAsString(bean, field);
        if (!GenericValidator.isBlankOrNull(value)) {
            final Pattern zipPattern = Pattern.compile("\\d{3}-\\d{4}");
            final Matcher matcher = zipPattern.matcher(value);
            if (!matcher.matches()) {
                addError(errors, field, validator, validatorAction, request);
                return false;
            }
        }
        return true;
    }
}

検証用メソッドを登録する

先ほど作成した検証用のメソッドを登録します。
具体的には、validator-rules.xml に登録することになります。
ここで、name属性の "zip" は、アノテーションクラスの@Validatorの引数と同じにします。
また、msg属性の "errors.zip" は、次に追加するエラーメッセージのキーとなります。

validator-rules.xml
…中略

     <validator name="zip"
            classname="takehiro.validator.CustomizedS2FieldChecks"
               method="validateZip"
         methodParams="java.lang.Object,
                       org.apache.commons.validator.ValidatorAction,
                       org.apache.commons.validator.Field,
                       org.apache.struts.action.ActionMessages,
                       org.apache.commons.validator.Validator,
                       javax.servlet.http.HttpServletRequest"
              depends=""
                  msg="errors.zip"/>

…中略

エラーメッセージを追加する

郵便番号の検証でエラーが発生した場合に表示するエラーメッセージを追加します。
先ほど書いたように、キー "errors.zip" は、validator-rules.xml の msg 属性と一致させます。

application_ja.properties
errors.zip={0}は郵便番号として不正です。
application.properties
errors.zip={0} is an invalid zip code.

Actionのpublicフィールドに、作成したアノテーションを追加する

今回は、SAStrutsチュートリアルに含まれる ValidatorAction を利用します。
以下のように、郵便番号を入力するフィールドを新しく追加します。

ValidatorAction.java
…中略

    @Required
    @Zip
    public String zip;

…中略

    @Execute(validator = false)
    public String index() {
        byteText = "1";
        shortText = "1";
        integerText = "1";
        longText = "1";
        floatText = "1.0";
        doubleText = "1.0";
        dateText = "2008/1/1";
        emailText = "higayasuo@gmail.com";
        urlText = "http://d.hatena.ne.jp/higayasuo";
        intRangeText = "7";
        longRangeText = "7";
        floatRangeText = "7.0";
        doubleRangeText = "7.0";
        minlengthText = "123";
        maxlengthText = "1234567890";
        minbytelengthText = "ああ";
        maxbytelengthText = "あああああ";
        phoneText = "03-9999-9999";
        zip = "999-9999";
        return "validator.vm";
    }
…中略

画面(vmファイル)を修正する

validator.vm
…中略

<tr>
<td>郵便番号:</td><td><input type="text" name="zip" value="$!zip"></td>
</tr>

…中略

以上で全ての準備が完了しました。

実際動かしてみる

無事動きました。エラーメッセージもちゃんと表示されます。

まとめ

実際のPJなどでは、今回のように独自の検証を行うケースがあると思うので
今回のような作業がマニュアル化されてると嬉しいですね。

SAStrutsのドキュメント内に記述がありました。以下のリンクの最後のほうを参照です。
id:higayasuo さん、ありがとうございます。
http://sastruts.seasar.org/featureReference.html#Validator

Seasar勉強会 in Sendai が開催されます!

東北デベロッパーズコミュニティから、Seasar勉強会 in Sendai の開催案内が来ました。

イベント案内 | 2008-06-14 (土) Seasar勉強会 in Sendai - 東北デベロッパーズコミュニティ(TDC)

早速、参加の申し込みをしました。
以下、タイムテーブルです。

14:00〜14:10 開催にあたって
14:10〜15:10 S2JDBCについて(ひがやすを様から)
15:20〜17:10 東北でのSeasar事例紹介(各30分ずつ)
17:20〜18:05 SAStrutsについて
18:05〜18:15 閉会&今後の予定について
18:30〜   懇親会

目玉は、Seasarプロジェクトチーフコミッタのひがやすをさんです。
東北デベロッパーズコミュニティ設立総会に参加した時はお話しする機会がなかったので、
今回はぜひともお話出来ればなと思います。

Effective Java Second Edition を入手

待望の Effective Java Second Edition を入手しました。

Effective Java (Java Series)

Effective Java (Java Series)

初版と比較して、ページ数としては252から346へ、項目数として57から78へ大幅に内容がパワーアップしております。

初版本の翻訳者である柴田芳樹さんのブログによると、翻訳作業はこれから開始するということで、日本語版が出るのはまだだいぶ先になりそうです。

http://blog.so-net.ne.jp/yshibata/2008-05-16/trackback

新しく追加された項目を中心に早速読み進めて行きたいと思います。

Working Effectively With Legacy Code が届きました

Amazonに注文していた本が届きました。

Working Effectively With Legacy Code

Working Effectively With Legacy Code

  • PART 1: The Mechanics of Change
    • Chapter 1: Changing Software
    • Chapter 2: Working with Feedback
    • Chapter 3: Sensing and Separation
    • Chapter 4: The Seam Model
    • Chapter 5: Tools
  • PART 2: Changing Software
    • Chapter 6: I Don't Have Much Time and I Have to Change it
    • Chapter 7: It Takes Forever to Make a Change
    • Chapter 8: How Do I Add a Feature?
    • Chapter 9: I Can't Get This Class into a Test Harness
    • Chapter 10: I Can't Run This Method in a Test Harness
    • Chapter 11: Need to Make a Change. What Methods Should I Test?
    • Chapter 12: I Need to Make Many Changes in One Area
    • Chapter 13: I Need to Make a Change, but I Don't Know What Tests to Write
    • Chapter 14: Dependencies on Libraries Are Killing Me
    • Chapter 15: My Application Is All API Calls
    • Chapter 16: I Don't Understand the Code Well Enough to Change It
    • Chapter 17: My Application Has No Structure
    • Chapter 18: My Test Code Is in the Way
    • Chapter 19: My Project Is Not Object Oriented. How Do I Make Safe Changes?
    • Chapter 20: This Class Is Too Big and I Don't Want It to Get Any Bigger
    • Chapter 21: I'm Changing the Same Code All Over the Place
    • Chapter 22: I Need to Change a Monster Method and I Can't Write Tests for It
    • Chapter 23: How Do I Know That I'm Not Breaking Anything?
    • Chapter 24: We Feel Overwhelmed. It Isn't Going to Get Any Better
  • PART 3: Dependency-Breaking Techniques

全434ページと、かなりのボリュームです。
ちょっとづつ読み進めて行きたいと思います。

「小飼弾のアルファギークに逢ってきた」を読みました

小飼弾のアルファギークに逢ってきた」を読みました。
発売直後、近くの本屋を梯子して探したのですが見つからなかったので、結局 Amazon で注文しました。

小飼弾のアルファギークに逢ってきた (WEB+DB PRESS plusシリーズ)

小飼弾のアルファギークに逢ってきた (WEB+DB PRESS plusシリーズ)

大変読み応えがありました。せっかくなので、各章で印象に残ったフレーズをメモ。

#0 Ruby on Rails開発者 David Heinemeier Hansson

David Heinemeier Hansson

我々は、簡単なことをより簡単にしたいと考えています。簡潔なものは簡潔なままに留めておきたいので、成長するに従って会社は当然大きくはなっていくけれど、なるべく少ない従業員でそれを実現したい。

#1 (株)はてなCTO 伊藤直也

伊藤直也

本当の意味で世界を幸せにするプロジェクトを完遂したら、その人は経済的に幸せになるべきであって、その手段としてベンチャーにいて、(その結果)ベンチャーが大きくなることもあるっていうのは、もっとみんなが知ってもいいことじゃないかって。

#2 Perl開発者 Larry Wall

Larry Wall

<<(私は)金持ちです。>> どれだけ持っているかではなく、どれだけ与えることができるかというのが金持ちの定義なら。

#3 livedoor 池邉智洋/谷口公一/ma.la

ma.la

土地に制約を受けている人が山ほどいるっていう感じがする。今地方で、ひっそりとコソコソ、何かおもしろいものを、1人で何か核兵器とか作っているような人がたぶんたくさんいるには違いないんだろうけど、そういう人たちが東京にいないと出会えない状況が、すごくもったいないと思う。そういう人は、ネットワークを通じてもうまくつながれないような気がする。それをネットワークが救うかっていったら、今のところあまり救えていない。救えていないから、それをなんとかしないとあれかなぁって。

#4 Twitter Co-founder Evan Williams

小飼弾

ウェブサイトを作るにあたって最も重要なことは? 2.0でなくてもいいので(笑)。

Evan Williams

情熱かな。できると見込んだらやる、という。人を見るときには、技術力よりもその人の持っている情熱に注目している。ウェブはまだ15歳ぐらいで、可能性はまだまだたくさんある。そういうところでは、「それを貫く能力」より「好きを貫く」力のほうがものをいう。「できるからやる」というのは、新しいことをやるには向いていない。

#5 The Seasar Project チーフコミッタ ひがやすを

ひがやすを

すごい技術があって、それに負けたままなんて悔しくないですか? そこは、かなわないからお前に任せたなんて、私は逃げたみたいでいやですね。競い合ってこそ、技術は向上していくんだと思います。Javaは、言語的にはHOT Deployのしくみは提供していませんが、工夫次第で何とでもなる。常に挑戦し続けたいですね。

#6 「達人プログラマー」著者 Dave Thomas

Dave Thomas

ソフトエンジニアというものはありません。少なくともまだないです。どういうことかというと、これ以上削れないところまで削るのがエンジニアリング。これ以上削れないところまで削るということは、どこまで削るとそれが壊れてしまうかというのが分かっているということです。まだソフトウェアに関しては我々はそのレベルには達していないんです。達していないから、まだソフトウェアエンジニアという言葉というのは嘘である。

#7 Pathtraq/Japanize 奥一穂

奥一穂

もう1つはじゃあ最初の母集団が尖っててそもそも問題なのかっていうところで、メディアの価値っていうのはデータを送る人と受ける人の落差なんですよ。そういう意味で言うと、みんなが使っていて、みんなが見たあとのページの情報をとっても実は全然メディアとしての意味がない。統計としては意味がありますけど、たぶんそれだとそんなに需要がない。だからやっぱりできるだけ、まずはニッチな人に使ってもらって、それまでニッチじゃない人にとって便利なツールになる。そういう形で、一番上にいる人たちと、その次の人たちの間でまずコミュニケーションができる。そういうふうにできればいいと思っているので、最初に尖っている人(のログ)が取れているっていうのは正解なんだろうと思っています。逆にそういう形にしないと、一気にマスを狙うっていうのはなかなか難しいですよね、資本的な問題とか、コミュニケーションの問題とか。

#8 Mozilla Corporation/jQuery開発者 John Resig

小飼弾

ただのエンジニアとすごいエンジニアを分けるものは何でしょう?

John Resig

あえて機能を追加しないことができる人がすごいエンジニアだと思います。すなわち具体的に何が重要であり、かつ何が重要でないかということを理解したうえで、その理解のもとで最適化ができる人。

#9 「Binary2.0」「スルー力」提唱者 高林哲

小飼弾

ハッカーとして、一番重要な技能って何だろう?

高林哲

「粘り強さ」じゃないですか。「深追い」ってぼくは呼ぶんですけど、問題を解決する場面で、わかるまで調べると。デバッグとかもそうじゃないですか、気分転換して、ぱっとひらめくときもありますけども、自分がしでかしたバグだったらけっこうひらめくこともあると思うんですけど、他人のプログラムって徹底的に調べていかないと…。

小飼弾

もう地の果てまでも。

高林哲

追いかけていかないといけないですよね。そのときにどこまで追いかけるかっていう、忍耐力っていうか粘り強さが必要かなと。ぼくなんかは、今日はもうこれでおしまいと諦めるときがあるんですけど、どんどん追いかける人は地の果てまで行っちゃうんで。たいしたもんだなと思います。

#10 Perl Mongers Ingy dot Net/Dave Rolsky/Jesse Vincent/C.L. Kao

DANKOGAI

「優れたエンジニア」として重要なのはどんなことだと思いますか?
(中略)

INGY

プログラマー以外の視点を持つっていうのもものすごい重要。たとえばCLKAOにとっての料理とか、Daveにとっての音楽とか。そうやって外からの視点を持っていると、ちゃんと使う人の意見に耳を傾けられる。

DROLSKY

コミュニケーションの能力はすごく大事で、ハッカーって聞くと部屋に閉じこもって引きこもってっていう印象があるんだけど、普通の人、プログラマーでない人に通じる話を持っているかどうかが、そのプログラマーのクオリティーを決める上で重要だと思う。

#11 IT戦士 天野仁史/こんにちはこんにちは! はまちや2

小飼弾

多くのユーザが利用するウェブアプリケーションですが、開発する上でもっとも重要なことは?

はまちや2

勢いですね!

天野仁史

あー勢い重要ですね!

はまちや2

何か思いついたら、その日のうちに、実際に動くプロトタイプを作っちゃうくらいの勢い。

小飼弾

作って出しちゃうっていうのは、本当重要だよね。それが本当に肌でわかったのは、ブログを書き出してから。変なクオリティのものを人様に出せるかいではなくて、出してもいいんですよ。人に突っ込まれたらそのときに直せばいい。

#12 (株)はてな 近藤淳也・令子×小飼弾・直美

小飼弾

だから僕は目下、子供を見ているときの一番の悩みっていうのは、どこまで子供に与えないようにしようかなということなんですよ、これはまじめに。それでも与えちゃったほうが大人は楽なんです。子供ができてから気がつきました。おもちゃって、大人のためにあるんです。要は子供が煩わしいからおもちゃでだますわけですよ。子供だましするんです。

#-1,#-2 スペシャル対談 きたみりゅうじ小飼弾に逢ってきた

小飼弾

人がいるところで最初に見るのは、他の人は何をしていないだろうかと、まだ何がなされていないかを真っ先に見るタイプですね。今、流行のKYで言えば、私も実は空気を読んでいないわけじゃないんですよ。読んで逆のほうへ行っているだけで。たぶん僕の強みは、何がないのかがわかることだと思うんですよ。何があるっていうのは普通の人でもよくわかる。でも僕は、何がないがわかるんですよ。この本には何が書いてません、がわかるんですよ。たぶん、それが僕の一番の強みです。

SAStruts + Velocity (2)

SAStruts + Velocity が動くことが確認出来たので、今度はSAStruts
チュートリアルに含まれるサンプルをVelocityに移植中です。

そこで、移植にちょっと手間取ったサンプルがありました。

「複数選択リスト」サンプルにて

「複数選択リスト」サンプルのJSPファイルは以下のようになっています。

multiselect.jsp
<html>
<head>
<title>Multiselect</title>
</head>
<body>
<html:errors/>
<s:form action="/multiselect">
<html:select property="select" multiple="true" size="3">
<html:option value="1">One</html:option>
<html:option value="2">Two</html:option>
<html:option value="3">Three</html:option>
</html:select>
<br />
${f:h(select)}<br />
<input type="submit" name="submit" value="サブミット"/>
</s:form>
</body>
</html>

まず、toolbox.xml に ListTool の定義を追加します。

toolbox.xml
 <tool>
   <key>list</key>
   <scope>application</scope>
   <class>org.apache.velocity.tools.generic.ListTool</class>
 </tool>

そして、multiselect.jspを元に、vmファイルを以下のように作成しました。

multiselect.vm
<html>
<head>
<title>Multiselect</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<form name="multiselectActionForm" method="post" action="$link.setRelative("velocity/multiselect/")">
<select name="select" multiple="multiple" size="3">
<option value="1"#if($list.contains($select, "1")) selected="selected" #end>One</option>
<option value="2"#if($list.contains($select, "2")) selected="selected" #end>Two</option>
<option value="3"#if($list.contains($select, "3")) selected="selected" #end>Three</option>
</select>
<br />
$!escape.html($!select)<br />
<input type="submit" name="submit" value="サブミット"/>
</form>
</body>
</html>

すると、以下のようなエラーが発生しました。*1

nvocation of method 'contains' in class org.seasar.struts.action.ArrayWrapper threw exception class java.lang.UnsupportedOperationException : contains

何でだろうと思い、ArrayWrapper#contains(Object)の実装を見てみたら、以下のようになっていました。

    public boolean contains(Object o) {
        throw new UnsupportedOperationException("contains");
    }

これが原因です。
どうしようかな…、ArrayWrapper#toArray()はサポートされていたので以下のように修正してみました。

修正後の multiselect.vm
<html>
<head>
<title>Multiselect</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<form name="multiselectActionForm" method="post" action="$link.setRelative("velocity/multiselect/")">
<select name="select" multiple="multiple" size="3">
<option value="1"#if($list.contains($select.toArray(), "1")) selected="selected" #end>One</option>
<option value="2"#if($list.contains($select.toArray(), "2")) selected="selected" #end>Two</option>
<option value="3"#if($list.contains($select.toArray(), "3")) selected="selected" #end>Three</option>
</select>
<br />
$!escape.html($!select)<br />
<input type="submit" name="submit" value="サブミット"/>
</form>
</body>
</html>

不恰好なコードですが、とりあえず動きました。
もっとスマートな方法は無いのでしょうか?

*1:SAStruts1.0.2-rc3 では、ArrayWrapper#contains(Object) がサポートされる予定になっています