goo blog サービス終了のお知らせ 

N2 ToolBox(跡地)

跡地です。引っ越しました。http://d.hatena.ne.jp/nosen

Groovy!

2004-06-05 02:07:56 | オープンソース
Javaで実装されたスクリプト言語といえば、BeanShell,Rhino, Jythonなどが有名ですね。僕はBeanShellが好きで結構使い込んでいたのですが、メソッド呼び出しにかっこをつけたりするのがだるくなってくることがありました。

このあたりの記述の簡単さ関してはGroovyのほうがよさそうです。GroovyはJavaに似た文法 + Rubyのような文法を特徴とするスクリプト言語で、僕のように仕事でJavaを使うRuby好きにはたまらない仕様となっております。

一番の特徴はなんといってもクロージャが使えることで、以下のようなコードが可能になります。

p = {o | println(o)}
p "Hello, World"

これで標準出力に"Hello, World"が出力されます。1行目でクロージャをpに代入して、2行目で呼び出しています。2行目はちょっと省略しすぎましたが、正式には

p.call("Hello, World")

と書くところです。クロージャ呼び出しのシンタックスシュガーとしてp("Hello, World")という書き方が出来、さらにGroovyではRubyのようにあいまいさのないかっこは省略できるので、かっこを省略してしまったと言う訳です。

クロージャは簡単に例えるとJavaの匿名内部クラスのようなものだと思えばいいでしょう。重要な違いは、Groovyのクロージャは遅延バインディングを許しているということです。これは、クロージャを定義した時点で定義されていない変数をクロージャの中で使うことが出来る、ということです。
たとえば以下のようなコードです。

p = {println(o)}
o = "Hello, World"
p()

このように、クロージャは、実際に実行された時点のスコープで実行されます。

セミコロン、かっこを省略できるというのも以外と重要です。人間、楽なほうに転がり落ちるのは簡単なもので、いったんこの書き方になれてしまうとなかなか元にはもどれません。

さらに、Groovyはバイトコードにコンパイルすることができます。ここまでくるとほとんどJavaとは違う新手の言語という感じですね。

JavaVM上で一旦Javaのクラスに変換されて実行されている都合もあってか、さすがにRubyほど動的で洗練された言語仕様にはなっていないですし、機能的にもおそらくBeanShellの方が上なのですが、これらの簡便な記法はかなりの魅力で、たとえば今までXMLで書いていた設定ファイル等をGroovyに置き換えたりする用途には最適かとおもわれます。

ServletとXWorkの橋渡し

2004-06-02 01:59:59 | オープンソース
久しぶりにWebWorkの話に戻ろうと思います。
XWorkがServlet APIに依存していないのはいいとして、どうやってWebWorkはHttpServletRequestや、HttpSessionのデータをXWorkに渡しているの?という声をいくつか聞きました。もっともな疑問だと思ったので、少しWebWorkのソースコードを追っかけて調べてみました。

結論からいうと、WebWorkのServletDispatcherというクラスがServletの世界からXWorkの世界への橋渡しをほとんど全てやっているようです。

XWorkのActionを実行するには、ActionProxyFactoryというクラスのcreateActionProxyを呼び出し、返ってきたActionProxyクラスのインスタンスのexecuteメソッドを呼び出します。
createActionProxyの引数は
  • ネームスペース
  • アクション名
  • ActionContextが使用するMap
の3つです。あとはXWorkが勝手にActionContextを生成し、設定ファイルの情報をもとに適切なActionを生成して実行してくれます。
ServletDispatcherがやってることは、HttpServletRequestからこれら3つの情報を引っ張りだすことだけです。

ここで3つめのMapにはリクエストパラメータ、リクエストのAttribute,
セッションのAttributeのMapが突っ込まれています。このMapを生成するときには、HttpServletRequest, HttpSessionのAttributeのエントリをまるごとコピーするようなことはせず、AbstractMapを継承した独自のMap実装の中にHttpServletRequest, HttpSessionをラップして、余計なオブジェクトの生成を回避しています。
Actionからここで設定したMapの情報にアクセスするには、ActionContextを使用する事ももちろん出来るのですが、その必要はほとんどないと思われます。リクエストパラメータはInterceptorによって直接Actionのプロパティに設定されますし、リクエスト、セッションのAttributeに関してもInterceptorにInjectさせるのがWebWork的に王道かとおもわれます。

ちなみにActionにSessionAwareというインターフェースを実装させるだけで、setSessionメソッド経由でセッションのAttributeのMapがInjectされます。

ほとんど無いとは思いますが、どーしてもHttpServletRequestをチョクで使いたい場合は、ServletRequestAwareを実装すれば良く、同様にしてServletContextをInjectすることも可能です。
ぬぅ。。。うつくしい。。

本当にWebWork/XWorkの設計は美しいです。ソースコードを追ってみるとそれが良く分かります。単純なコーディングスタイルの統一感からいったらStrutsなどJakartaのプロダクトの方が上かも知れませんが、設計の素性の良さがあらわれているためか、ソースを読んでいて迷子になることがほとんどありません。プログラマの意図が1クラス、1メソッド、1ステートメント毎に明確に刻み込まれているのを感じるのです。

僕も見習いたいものです。

他にも疑問がありましたらどしどしコメントください。
頑張って調べます。

XWork用語集2

2004-05-25 00:53:00 | オープンソース
とりあえず続きです。

Package
Action,Result Type,Result ,Result Type,Interceptor,Stackをひとまとめにグループ化したものです。Packageは他のPackageを「拡張」することができるため、設定情報の再利用が容易になっています。

Value Stack
リクエスト毎に生成され、Interceptor,Action,Resultが互いにやりとりするデータを格納するコンテナとなるスタック実装です。InterceptorへのアクセスにはOGNLという式言語が使用されます。

Component
XWorkにおけるDependency Injectionパターンの適用です。Actionに対してComponentをInjectすることができます。

ここまで説明した内容を図示しておきます。
あまり自信がないので間違ってたらつっこんでください。

XWork用語集1

2004-05-24 03:06:26 | オープンソース
WebWorkを理解するためには、ベースとなっているXWorkを理解することが大切だと思います。そこでここでXWorkを理解するのに必要な用語を一通りおさらいしておきたいと思います。

Actions
Actionはリクエスト毎に呼び出されるクラスで、あるひとまとまりの処理を行った後、Resultを返します。Actionはフレームワークから呼び出されるエントリポイントとして最低限execute()メソッドを実装します。

ActionContext
ActionContextは実行環境へのアクセス手段を提供します。HttpServletRequestやHttpSession等のServlet特有のAPIをラップしているイメージで良いと思います。その他にもXWork特有の実行環境へのアクセスパスも提供してはいますが。ActionContextはリクエスト毎にインスタンス化されます。

Interceptor
InterceptorActionの呼び出しの前後に任意の処理を動的にはさみこむオブジェクトです。前にも書きましたが、ServletFilterと同じイメージです。複数のActionにまたがった共通の処理を定義するときに使用します。

Stack
Stackは複数のInterceptorの順序つきリストです。Interceptorをグループ化してまとめてActionに適用するときに使います。StackActionの設定時に名前で指定できます。

Result
ResultはActionが返す文字列定数です。標準的な定数としてlogin, error, input, none, success が定義されていますが、もちろん他のものを定義するのは自由です。

Result Type
Viewを生成するクラスです。WebWorkのデフォルト設定ファイルでは前もってdispatcher,redirect, velocity, chain, xsltが定義されています。
普通にJSPでViewを生成するときに使うのがdispacher、Velocityの時はvelocityなのですが、redirect, chain, xsltというのはちょっとまだ良く分かりません。興味津津です。

最小Webアプリ2

2004-05-21 02:14:52 | オープンソース
続きです。

xwork.xmlの作成
以下の通りXWorkの設定ファイルを作成します。

<xwork>

  <include file="webwork-default.xml"/>

  <package name="default" extends="webwork-default">

    <default-interceptor-ref name="defaultStack"/>

    <action name="Hello" class="wwstudy.HelloAction">
      <result name="success" type="velocity">
        <param name="location">/WEB-INF/vm/hello.vm</param>
      </result>
      <interceptor-ref name="defaultStack"/>
    </action>
  </package>
</xwork>

include要素で指定しているwebwork-default.xmlはWebWorkが提供するデフォルトのXWorkの設定ファイルで、WebWorkが提供するインターセプタなどが定義されております。XWorkではこのように他の設定ファイルを設定のベースラインにすることができます。
キモになるのはaction要素で、ここで自分で作ったActionの設定を行います。result要素のname属性は先程のActionのexecuteメソッドの戻り値と整合性がとれている必要があります。intercepter-ref要素で参照しているのはincludeしているWebWorkの設定で定義されているデフォルトのインターセプタスタックです。

デプロイ
これらのファイルを必須ライブラリと共にWebアプリケーションの形にまとめます。ディレクトリ構造は以下の様になります。
`-- WEB-INF
    |-- classes
    |   |-- wwstudy
    |   |   `-- HelloAction.class
    |   `-- xwork.xml
    |-- lib
    |   |-- commons-logging-1.0.3.jar
    |   |-- ognl-2.6.3-modified.jar
    |   |-- oscore-2.2.1.jar
    |   |-- velocity-dep-1.3.1.jar
    |   |-- webwork-2.0.jar
    |   `-- xwork-1.0.jar
    |-- vm
    |   `-- hello.vm
    `-- web.xml


動かしてみる
さあ、それでは早速動かしてみましょう。今回は、jdk1.4.2_04とTomcat 5.0.24を使います。Tomcatを起動して、ブラウザで

http://localhost:8080/wwblog/Hello.action?name=Nose

を表示すると、ちゃんと

こんにちは! Nose さん.

と表示されます。

気になること
これでWebWorkを使ったWebアプリの概要は大体分かったのですが、細かい所がまだまだ不明です。検証や、型変換のときにエラーがおこったらどう処理するのでしょうか?セッションの扱いは?共通のデータ構造を複数のActionで使いたい時は?などなど。
これからはそういう細かい所をすこしづつ調べて行こうと思います。

あと、今回WebWorkの挙動で気になったのは、executeの戻り値の文字列がxwork.xmlの該当Actionの設定に存在しないと、ブラウザにレスポンスが永遠に戻ってこなくなることです。ログに警告ログははかれるのですがね。。。これって挙動としてはすこしおかしい気がします。
このへんについてももう少し突っ込んで原因をさぐっていきたいです。

ではまた。

最小Webアプリ1

2004-05-21 01:47:46 | オープンソース
さて、理屈はこれくらいでおいといて今日は実際にWebWorkを使って以下のような非常に簡単なWebアプリを作ってみます。

・リクエストパラメータ"name"から値をとってきて「こんにちは! xxさん」という挨拶を表示する

以下の手順で作業をすすめることにします。
1. Actionの作成
2. Velocityテンプレートの作成
3. web.xmlの作成
4. xwork.xmlの作成

Actionの作成
以下の通りActionインターフェースを実装したクラスを作成します。
package wwstudy;

import com.opensymphony.xwork.Action;

/**
 * Hello, World
 */
public class HelloAction implements Action {
    /**
     * Name
     */
    private String name;
    /**
     * Performs the simple action
     */
    public String execute() {
        return SUCCESS;
    }

    public void setName(String s) {
        name = s;
    }

    public String getName() {
        return name;
    }
}

リクエストパラメータ"name"を受け取るためにプロパティnameが定義されています。executeメソッドの戻り値は処理結果を表す任意の文字列で、XWorkはこの値と設定ファイル(xwork.xml)をもとにビューを選択します。StrutsのActionForwardと同等のものですね。SUCCESSというのはActionインターフェースで定義されている定数で、処理結果としてありがちなものとしてほかにERROR, INPUT, LOGIN, NONEが定義されています。

Velocityテンプレートの作成
今回は私の好みでビューにはVelocityを使いますが、もちろんJSPを使うこともできます。Velocityテンプレートは以下のようになります。
こんにちは! $name さん. 

$nameでActionのNameプロパティが参照できます。Velocityについてはこちらを参照願います。 JSPのかわりに使えるとても使いやすいテンプレートエンジンです。個人的にかなり好きです。

web.xmlの作成
web.xmlに以下の通りWebWork固有の記述を追加します。
    <servlet>
        <servlet-name>webwork</servlet-name>
        <servlet-class>com.opensymphony.webwork.dispatcher.ServletDispatcher</servlet-class>
    </servlet>
    <servlet>
        <servlet-name>velocity</servlet-name>
        <servlet-class>com.opensymphony.webwork.views.velocity.WebWorkVelocityServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>webwork</servlet-name>
        <url-pattern>*.action</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>velocity</servlet-name>
        <url-pattern>*.vm</url-pattern>
    </servlet-mapping>


Strutsとの違い2

2004-05-20 00:36:03 | オープンソース
引続き、Strutsとの違いです。

3. ActionFormが、ない
WebWork/XWorkには、StrutsでいうところのActionFormにあたるものがありません。では、HTTPリクエストのパラメータをどうやって受け取るのかというと、昨日の話の通りActionがリクエスト毎にインスタンス化されるというのがミソで、Actionのプロパティに設定されてしまいます。なので、ActionFormにあたるものがない、というよりもActionFormとActionが一体化しているというほうがしっくりきますね。

4. 前処理/後処理
StrutsではActionに対して共通の前処理、後処理を設定しようとすると、Actionのクラス階層を形成して共通処理をスーパークラスにもっていく必要がありました。
これに対してWebWork/XWorkではServletFilterのようなChain Of Responsibilityの形をとる"Intercepter"というコンポーネントによってActionの前処理、後処理を行います。Interceptorのチェーンの構成は設定によってActionごとに変更することができるので、とても便利です。WebWork/XWorkではリクエストパラメータの検証やActionのプロパティへの設定もInterceptorとして実装されています。

細かいことをいえばいろいろあるのでしょうが、ざっと目についた違いはこんなところでしょうか。こういう風に書くと、なにかStrutsに恨みでもあるのかと思われそうですが、そんなことはありません。単に私がStrutsと比較しながら勉強した方が分かりやすかっただけなので、誤解なきようお願いします。
まあWebWorkの方が後発なのですし、きれいな作りになっているのは当然といえば当然ですよね。。。それもこれもStrutsという偉大な先輩あっての成果なのではないでしょうか。
なお、昨日と今日かいた内容は以下のページを参考にしています。

WebWork vs Struts

そろそろ実際にWebWorkを使って、何か動くコードを書いて行きたいと思います。

Strutsとの違い1

2004-05-19 01:23:04 | オープンソース
WebWorkとStrutsはよく似たフレームワークです。以下の機能は両者に共通するものです。

・リクエストされたURIと設定ファイルに基づいて適切なActionに処理を転送する。
・HTTPリクエストパラメータを利用しやすい形のオブジェクトに変換してActionに引き渡す。
・必要に応じてHTTPリクエストパラメータの検証を行う。

一方、以下の点ではWebWorkはStrutsと異なっており、独自の特色を打ち出しています。

1.ServletAPIへの依存性が低い
WebWorkはXWorkという汎用的なコマンドパターンのフレームワークをベースにしています。StrutsでのActionクラスにあたる部分は完全にXWorkの中になります。XWorkはServletのAPIに依存していません。WebWorkというのは、大雑把にいうとこのXWorkをServlet経由で呼び出せるようにしたものです。

2.Actionクラスのライフサイクル
WebWork(正確にはXWork)にもStrutsのAcitonクラスに相当するActionインターフェースというのが存在しますが、これはリクエスト毎にインスタンス化されます。StrutsでActionクラスのインスタンスが使い回されているのは主にパフォーマンス上の理由だと思われますが、これに対してWebWorkは、どうせServletコンテナはリクエスト毎に山のように一時オブジェクトを作るのだから、もう一個余計に作ったところでどうってことねぇよというスタンスをとっているようです。この割り切りによって、StrutsのようにActionが必要とするデータの全てを引数として受け取る必要がなくなり、Actionインターフェースは以下の通り随分シンプルな作りになっています。
public interface Action extends Serializable {
     String execute() throws Exception;
}

別に話を簡単にするためにメソッドを省いている訳ではなく、本当にこれだけなのです。あとは便宜的に定数がいくつか定義されているだけです。引数が4つもあるStrutsのexecuteメソッドとは対象的ですよね?

眠くなったので、続きはまた明日。

続くか?

2004-05-17 23:17:49 | オープンソース
僕もいろいろ調べた技術的なこととか、記録していこうと思って
はじめてみました。はたして続くのでしょうか?
内容はどうしてもオープンソース系Javaの話が多くなるでしょうねぇ。

最初のテーマとしてしばらくWebWork2を調査して行こうと思っております。まずは、何でWebWork2なのかという話をひとくさり。

最近久しぶりにWebアプリケーションを作成する機会があり、初めて(!)Strutsを使ってみたのですが、これがイマイチ気に入りませんでした。細かい点はいろいろあるんですが、一番気になったのは、主にActionがサーブレットのインターフェースに依存してることが原因でものすごくテストしづらいこと。StrutsTestCaseみたいにMockObjectアプローチでいくにしても、Cactusみたいにコンテナ内でテストするにしてもなかなか大変な作業になってしまうんですよね。世の中あふれてるStrutsの情報の中でもこういうノウハウって少ない気がするんですが、みんなどうしてるんでしょうかね?

で、最近話題のWebWork2はどうなんだろうと調べ初めたところ、これが大変良くできたものであることがすぐにわかりました。サーブレットの世界とコマンドパターンのフレームワークの世界がきれいに切り離されており、APIはシンプルかつ柔軟性にも富んだ、非常に美しいフレームワークだと思いました。
できれば実際のプロジェクトに適用してみたいのですが、そのためにはまず自分の周りの人達にその良さを伝えていかねば。その時に自分が調べたことの記録が残ってると手間が省けていいね。という訳でいまさらながらこのようなことを始めてみたわけです。

が、予定はすべて未定なので、本当にその志が全うできるのかは極めて不透明な状況となっております。