トップ «前の日記(2008-01-04 (Fri)) 最新 次の日記(2008-01-22 (Tue))» 編集

ふたつの川うるおう日記


2008-01-20 (Sun)

_ [Java][Seasar] Cubby 1.0.0-RC1 リリース

Cubby 1.0.0-RC1がリリースされました。詳細はid:agtさんのCubby 1.0.0-RC1 リリース案内をどうぞ。今回のバージョンから@Urlアノテーションが@Pathと@Acceptに変更・分割されたので、既に使っていた方は書き換える必要があります。@AcceptはPOSTやGETなどのリクエストメソッドを限定する新しい仕様なので、今までと同じPOSTもGETも区別しない挙動で良ければ、@Urlを@Pathに置換でサクっと書き換えるだけでOK。

| Bookmark:

_ [Java][Seasar] Cubbyの良いところ

先日Cubbyで作った2個目の簡単なアプリを予定通り動かし始めました。今のとこ大きなトラブルもないようです。というわけで、Cubbyの良いところを書きたいと思います。ちなみにほとんど同じことがSAStrutsにも言えます。

  • Examplesが実用的で簡単

Cubbyに興味を持ったらとりあえずサンプルwarを動かし、Todoサンプルアプリケーションを見てみてください。一覧表示・詳細・追加・編集・確認・保存に加えて、ログインとログイン状態確認(AuthActionInterceptor)もついています。そして、それぞれの機能に対応するTodoListAction、TodoAction、LoginActionを見ると、とてもコードが少なく、これならすぐに似たようなアプリが作れる気がします(実際すぐに作れました)。

さらにコードを良くみると、フォーム=アクション自身になっています。これは僕の中でかなり革新的でした。今までフォーム用にPOJO(Entityを継承したりして)を用意してたけど、結局僕のように小さなアプリしか作らない場合、POJOを使いまわすなんてことは無く、それだったらアクション自身にあった方が見通しが全然良いんです。なお、別に分けたい場合は@Formで別途指定できるので、フォーム=アクション自身に絶対する必要があるというわけでもありません。

他にもCubbyのExampleは小さなテクニックがいろいろ散りばめられています。例えば、エラー時の画面装飾。effects.jsでちょっと目に付いて親切な装飾がされています。もちろんこれはCubbyでしかできないことではないですがこういったちょっと「良いな」っと思えるサンプルがいろいろあります。そんなわけでますますCubbyを使ってみたいと思ってしまうわけです。

また、今のExampleの例では、S2DxoとS2Daoを使っていますがここをBeansとS2JDBCにしたって問題ありません。僕はそうしています。

  • URIが綺麗

やはりURIは、名は体を表す如く美しくあるべきだと思います。以前、無設定S2Struts用にSplitUpperActionPathNamingRuleというのを取り入れていただきましたが、それでも自由自在というところまではいきませんでした。Cubbyでは@Pathにより、URIの一部分をリクエストパラメータにするなど思い通りにURIを表現できます。この仕組みは、RequestRoutingFilterというフィルタにより、実体のアクションクラスへフォワードすることで実現されています。この仕組みも最初見たときそんな手があったかーっと驚きました。

また、通常認証確認処理はURI単位で設定したいので今まではオリジナルのフィルタで書いていました。StrutsなどでInterceptorを使ってやろうとすると、アクションに引っ掛けることになるので、.jspなどのページに直接アクセスされると認証処理が掛けられないので困ってしまいます。でも、Cubbyを使ったアプリでユーザがアクセスするのはすべて綺麗なURIにすることができ、実体のページにアクセスさせないようにしやすいです。なのでExampleのAuthActionInterceptorのようにInterceptorでも抜けのない認証確認が出来ます。ちなみにこのInterceptorはcustomizer.diconでLoginAction以外のアクションに適用するように設定されています。サンプルのAuthActionInterceptorをちょっと拡張すれば、URIごとにアクセス権限を分けるといったことも簡単に実現できます。

public Object invoke(MethodInvocation invocation) throws Throwable {
    // 実行しようとしているアクションを取得
    final Action action = (Action)invocation.getThis();
    // リクエストされたURLを取得
    final String url =
        (String)request.getAttribute("javax.servlet.forward.servlet_path");

    // 非ログインページ
    if (url.startsWith("/index")) {
        return invocation.proceed();
    }
    ...
    // ログインページ
    // ログイン状態の確認
    final PosixAccount user = (PosixAccount)sessionScope.get("user");
    if (user == null) {
        action.getFlash().put("notice", "ログインしていません。");
        return new Redirect("/login/");
    }
    ...
    // 各ページへのアクセス権限の確認
    Object group;
    // admin
    group = sessionScope.get("group_admin");
    if (url.startsWith("/admin/") && group == null) {
        return new Redirect("/");
    }
    ...
    return invocation.proceed();
}
  • @Pathによる制約

「URIが綺麗」に関係しますが、@Pathで指定する値には正規表現で制約を付けることができます。簡単な例だと次のとおりです。

public class TodoAction extends Action {
    public Integer id;
    @Path("{id,[0-9]+}")
    public ActionResult show() {
        Todo todo = todoDao.selectById(this.id);
        todoDxo.convert(todo, this);
        return new Forward("show.jsp");
    }
}

これだと、/todo/1 にアクセスすれば、idに1が自動的に入った状態でshow()が実行されます。/todo/aa の時はshow()は実行されません。この例はアクションメソッドに@Pathを付けていますが、クラス自身に@Pathを付けることもできます。

僕が使った複雑な例としては、こんな感じです。

@Path("admin/{thesisTypeName,abstract|full}/{userId}")
public class AdminThesisAction extends ThesisAction {
    ...
    @Validation(rules = "submitValidation", errorPage = "/admin/index.html")
    public ActionResult submit() {
        super.submit();
        return new Forward("/admin/submit.html");
    }
    ...
    public ActionResult download() {
        File file = new File(saveDir, thesis.getFileName());
        if (!file.exists()) {
            flash.put("notice", "ファイルが見つかりません: " + file.getName());
            return new Redirect("/admin/");
        }
        return super.download(file);
    }
}
public class ThesisAction extends DownloadAction {
    public String thesisTypeName;
    public String userId;
    ...
    public ActionResult submit() {
        if (thesis != null) {
            Beans.copy(thesis, this).excludesNull().execute();
        }
        return new Forward("submit.html");
    }
    ...
}
public class DownloadAction extends Action {
    public HttpServletResponse response;
    protected ActionResult download(File file) {
        ...
    }
}

この例だと、/admin/abstract/user001/submitや/admin/abstract/user099/downloadなどがAdminThesisActionですべて処理されます。extends ThesisActionとしているのは同じようなことをユーザごとや特定の権限を持った人も同じ機能を使いたかったからです。 実際に@Path("student/{thesisTypeName,abstract|full}")を持つStudentThesisActionと@Path("faculty/{thesisTypeName,abstract|full}/{userId}")を持つFacultyThesisActionを作りました。権限ごとに項目が多少違うので、validationはそれぞれの権限に応じたものをそれぞれの実装クラスで定義しました。

  • Validationが柔軟

CubbyではValidationはValidationRuleをValidationRulesでひとまとめにして定義し、それぞれのアクションメソッドにそれぞれ適用できます。つまり、アクションメソッドごとに違うValidationを適用できます。アクションのコードが少ないのはここでも役に立ちます。ExampleのLoginActionを見れば判りますが、アクション内にオリジナルのValidationRuleがあります。ここでも僕のように小さなアプリで使いまわすことがないなら同じアクション内の方が見通しが良いです。しかも同じアクション内にあることでアクションにあるパラメータもそのまま使えます。

@Binding(bindingType = BindingType.NONE)
public ValidationRules confirmValidation = new DefaultValidationRules() {
    @Override
    public void initialize() {
        add("token", new TokenValidator());
        add("enTitle", new RequiredValidator());
        add("enTitle", new RegexpValidator("\\p{ASCII}+"));
        add("jaTitle", new RequiredValidator());
        add("pageSize", new RequiredValidator());
        add("file", new RequiredValidator());
        add("file", new FileRegexpValidator(".+\\.pdf"));
    }
};

Cubbyが提供するカスタムタグは親切な設計になっています。特にお気に入りなのが、tokenです。ページのform内に <t:token /> と書いて、ValidatgionRulesに add("token", new TokenValidator()); するだけで、2重サブミット・CSRF対策が出来てしまいます。また、カスタムファンクションのoddもテーブルなどで色分けが簡単に出来るようになっていて親切です。

  • プレゼンテーションがJSPである = Mayaaが使える

個人的にフレームワークを選ぶ基準の一つにMayaaが使えるかどうかというのがあります。Mayaaは「プログラマとデザイナの作業分担を強く意識したWEBフロントサービスエンジン」という説明がありますが、一人で作る場合でも有効です。主な理由は2つです。1つ目はそのページの機能が一目瞭然になること。2つ目はコンポーネントによる機能分割が可能なことです。

1つ目は、僕の記憶力が良くないことに関係します。たいてい1日経たずしてそのページが何の機能を持ってたか綺麗に忘れます。そんな時、まず.mayaaを見ます。.mayaaにはそのページで実現される「機能」しか書かれていません。それもたいていたいした量はありません。そんなわけで、一目瞭然にそのページの機能が判ります。そして機能を把握したら、頭をデザイナモードにして.htmlを編集するか、プログラマモードにしてアクションを編集します。記憶力が良い方でもおそらく1年も経てばすっかり忘れてしまうと思います。そんな時、.mayaaはきっと役に立つはずです。

2つ目は、1つ目にも関係しますが、まとまった機能・使いまわす機能はコンポーネントにして、<m:insert>を使えば、複雑なページでも.mayaaが簡潔になります。簡潔になれば、1つ目のように機能を確認する時にすぐに把握できます。先程の@Pathのとこで出てきた複雑な例は、ThesisActionを継承したクラスが3つありました。フォームの項目に1つか2つの違いはありますが基本的に同じです。そんなわけでそれらに対応したフォーム部分は共通のコンポーネントに切り出してしまいます。

他にも、m:extendsでページデザインを楽に出来たり、やろうと思えば.mayaa単体でアクション相当のことが出来てしまうなどMayaaは超強力です。そんなわけで、フレームワークを選択する時、僕はMayaaが使えるかどうかは重要な判断基準になります。

  • 何はともかくS2ContainerのHOT deploy, publicフィールド, Beans, S2JDBCなどなど

CubbyとMayaaの良いところを書きましたが、忘れちゃいけないのがS2Containerです。アクションのコードを小さく書けるのは、Cubbyだけでなく、S2Containerの多くの機能があるおかげです。S2ContainerというとDI・AOPと想像される方もいるかもしれませんが、S2Containerのすごいとこは、DI・AOPと同じかそれ以上にその周辺の様々な強力な機能群があることだと思います。Cubbyの内部でもリクエストパラメータを処理するのにS2Dxoが使われていたり、CubbyやSAStruts利用者がアクションメソッドを小さく書くのに、publicフィールド, Beans, S2JDBCは必須機能です。これらはきちんと型を理解して処理してくれるのも大きな魅力です。また、サクサク開発するのにHOT deployは欠かせません。

というわけで、長々書きましたが、今現在僕には、Cubby + S2ContainerのHot deploy, publicフィールド, Beans, S2JDBC + Mayaaがちょうど良いのです。

他にも書いてない良いとこもあると思いますがとりあえずこんな感じです。Cubbyの代わりにほぼ同様のことのできるSAStrutsを選択するのも良いと思います。CubbyとSAStruts、まだ触ってない方は是非触ってみてください。

| Bookmark:
[]

| Return to page top | Vicuna CMS - WordPress Theme - Vicuna Ninja Style for tDiary |