2007年5月30日水曜日

grail-0.5 bookmarks

杉浦さん@それなりの日記の記事に付け加えて...



grails-0.5 の例題として付いてくる bookmarks を動かすには, 以下のプラグイン



  • http://grails.org/XFire+Plugin


  • http://sky.geocities.jp/acegiongrails/

をインストールする必要があるみたい (acegi は grails-0.5+ 対応版). それぞれのプラグイン (.zip) をダウンロードしたら,



% cd .../bookmarks

% grails install-plugin .../grails-xfire-0.5.zip

% grails install-plugin .../grails-Acegi-0.1.zip



すると, run-app できるようになる (それでもなんだか例外を吐いているようだけど:-)



grails-0.5 では 今までの domain-class と同じように hibernate annotation を処理できるようだ (実際ちゃんと永続化されているように見える). ただしそのためには, Java でアノテーション付きのコードを書かないといけない (.../src/java/org/grails/*.java). もっともアノテーションの処理は hibernate に任せているようで, grails のコードにはない.

SS2007

えーと, 告知です...



6 月 27日 (水) から 29日 (金) の三日間にわたって, 新潟市朱鷺メッセで「ソフトウェア・シンポジウム 2007」が開かれます.



初日 27 日に不肖山田が, 2 時間程度のチュートリアル「日常会話的モデル駆動開発 (Groovy/Grails を使ってみる) 」をやることになっています.



中日 28 日は各ワークショップに分かれて一日中議論を行います. 山田はモデリング・ワークショップに参加します.



参加受付は http://www.sea.jp/Events/symposium/ss2007/ss2007cfp.html, 詳細は SS2007 Portal をご覧ください.



日々の業務の中で何か納得のいかないものを抱えている方, このまま行ったらこの業界先はないぞと考えている方, あんまり大きな声では言えないこんな面白い話があるんだけどさという方, どなた様も参加をお待ちしています.



2007年5月29日火曜日

GrailsPlugin のプロパティ

GrailsPlugin のプロパティを GrailsPlugin.java からまとめておく. ただしドキュメントとは幾分異なっている. 5.5 辺りで実装される予定らしい.




doWithDynamicMethods


実行時にメソッドを inject するために呼ばれるクロージャ.


watchedResources


このプラグインが変化を監視するリソース.


evict


このプラグインが立ち退かせるプラグインのリスト. 立ち退きは PluginManager がロードするときに行われる.


status


このプラグインの状態. "enabled" (ロードされる), "disabled" (ロードされない)


influences


このプラグインが影響を与えるプラグインのリスト. 監視しているリソースが変化すると, 影響を及ぼすプラグインは refresh() される.


onChange


監視しているリソースが変化したときに呼ばれるクロージャ.


doWithWebDescriptor


web.xml の作成時に呼ばれるクロージャ.


version


このプラグインのバージョン.


doWithSpring


Spring の実行時コンフィギュレーションが行われるときに呼ばれるクロージャ.


doWithApplicationContext


初期化が終わり, アプリケーション・コンテキストが確立したときに呼ばれるクロージャ.


dependsOn


このプラグインが依存している他のプラグイン.


artefacts


このプラグインがサポートする ArtefactHandler のリスト.

このほかにも予定されているが, grails-0.5 では実装されていないプロパティがかなりあるようだ.

grails-wicket plugin を読む (3)

さて, メタオブジェクトの迷宮からようやく戻ってきた. 残りは wicket を動かすための手続きを grails のコードとして書いているだけだ. WicketGrailsPlugin.groovy に戻ろう.



def doWithSpring = {

def applicationClass = application.allClasses.find { Application.class.isAssignableFrom(it) }



if(applicationClass) {

applicationBean(applicationClass)

}

}




applicationGrailsApplication だということが分かった. そうするとここには「この Grails アプリケーションが持つ (ロードした) すべてのクラスの中で wicket.Application クラスと (ほぼ) 同じクラスがあれば, それを applicationBean としてインスタンス化する (ここで Spring Bean Builder を使っている)」と書いてあることになる. その「wicket.Application クラスと (ほぼ) 同じクラス」というのが, grails-app/controllers/WebApplication.groovy なわけだ.



def doWithWebDescriptor = { xml ->

def servlets = xml.servlet[0]

servlets + {

servlet {

'servlet-name'('wicket')

'servlet-class'('wicket.protocol.http.WicketServlet')

'init-param' {

'param-name'('applicationFactoryClassName')

'param-value'('wicket.spring.SpringWebApplicationFactory')

}

'load-on-startup'(1)

}

}



def mappings = xml.'servlet-mapping'[0]

mappings + {

'servlet-mapping' {

'servlet-name'('wicket')

'url-pattern'('/app/*')

}

}

}




doWithWebDescriptor は (アプリケーション起動時に Grails によって動的に作られる) web-app/WEB-INF/web.xml に関するクロージャ. web.xml の作成時に呼び出され, web.xml に以下を付け加える.



<servlet>

<servlet-name>wicket</servlet-name>

<servlet-class>wicket.protocol.http.WicketServlet</servlet-class>

<init-param>

<param-name>applicationFactoryClassName</param-name>

<param-value>wicket.spring.SpringWebApplicationFactory</param-value>

</init-param>

<load-on-startup>1</load-on-startup>

</servlet>



<servlet-mapping>

<servlet-name>wicket</servlet-name>

<url-pattern>/app/*</url-pattern>

</servlet-mapping>


こんなくそ XML を書かずに済んでいるのは Groovy の Markup Builder のおかげだ.



で, この plugin がやっているのはこれだけ. つまり普通の J2EE アプリケーションで Wicket を使うときにやる設定の手続きを WicketGrailsPlugin.groovy に書いているのだ. 後は grails の枠組み (特に GORM) がすべてそのまま使えている.



よくできました.



grails-wicket plugin を読む (2)

2回目. ここでは実は "Convention over Configuration" の実態が実は, grails がインタプリタ (メタオブジェクト) の集合になっていることだというのが分かる. ではいってみよう...



プラグイン定義クラスの規約を決めているやつは, 実はここにいる.



org.codehaus.groovy.grails.plugins.GrailsPlugin



Java インタフェースだが, スーパーインタフェースは Spring Framework の中にある ApplicationContextAware. ApplicationContextAware を実装するクラスは Spring Framework の恩恵を得ることができる, ととりあえずは思っておく.



GrailsPlugin には「規約」に相当する (≒ Java Interface に相当する), つまりプラグイン定義クラスで def すべきプロパティが文字列として定義されている. 例えば



String DO_WITH_DYNAMIC_METHODS = doWithDynamicMethods;

String WATCHED_RESOURCES = watchedResources;

String EVICT = evict;

// ......




また実装クラスはそれらプロパティへのアクセッサを定義しなさい, となっている. 例えば



void doWithWebDescriptor(GPathResult webXml);

String getName();

String getVersion();

// .......




つまり GrailsPlugin の実装クラスは, プラグイン定義クラスのインタプリタなのだ!



GrailsPlugin を実装しているのは



org.codehaus.groovy.grails.plugins.AbstractGrailsPlugin



で, つまりプラグイン定義クラスのインタプリタのひな形というわけ. これはさらに AbstractGrailsClass のサブクラスになっている. AbstractGrailsClass は Grails 全体で使われる汎用インタプリタのひな形だ. AbstractGrailsClass のサブクラスには他に DefaultGrailsDomainClass などがあるが, これはドメイン・クラス定義クラスのインタプリタということになる.



org.codehaus.groovy.grails.plugins.DefaultGrailsPlugin



AbstractGrailsPlugin を具体化したもの, つまりデフォルトのプラグイン定義クラス・インタプリタと言える. この中身を見てみると, new するときには対象となるプラグイン定義クラス (例えば WicketGrailsPlugin) が渡される. これがつまりインタプリタに与えられるプログラムということになる. このプログラムのシンタックスは前述の GrailsPlugin が決めているわけだ.



だから, プラグイン定義クラスに定義されていない変数 application が出現したとしても, それをどうするかはインタプリタの都合次第ということになる. インタプリタの中を見てみると application という変数には GrailsApplication が入っている (doWithRuntimeConfiguration() メソッド内). 他にもこのクロージャの中では manager, plugin, parentCtx, resolver という変数が使えることが分かる.



GrailsApplication は多分 Grails アプリケーションのファサードだろうと想像がつく. けど, これが実際にどんな変数とメソッドを持っているかは, GrailsApplication の JavaDoc のようなドキュメントかソース・コードを見ないと分からない. そこが Grails の弱点だとは思う. つまりインタプリタの世界 (Java) とプログラムの世界 (Groovy/Grails) が分離している. だから何かしようと思うと二つの世界を行ったり来たりしなければならないことになる. Lisp や Smalltalk だとこの二つの世界は同じ言語で記述され, かつ優秀な開発/デバグ環境が存在するのだが...



ちなみにここで「インタプリタ」と呼んでいるものは「メタオブジェクト」と呼び変えてもよい. オブジェクトの挙動を決めているオブジェクトだから.



続く...



grails-wicket plugin を読む (1)

せっかくだから grails-wicket プラグインのソース・コードを読んでみる. たった40行くらいだし. ただし自分も修行中の身ゆえ, 間違いあればお許しのほどを.


grails-wicket プラグインのコードはここ. 本質的にはこの WicketGrailsPlugin.groovy だけ.



一行ずつ読んでいくことにする.



def version = 0.1



バージョンを指定している. これはプラグイン・パッケージを作る (% grails package-plugin) と自動的にパッケージ名 (grails-プラグイン名-バージョン.zip) に含まれる. そのほかにも他のプラグインへの依存性を書くときに使われる (後述). "0.1" とか "1.0" とか書くのが普通だろうけど, 文字列なら多分なんでもいい.



def dependsOn = [:]



このプラグインが依存している他のプラグインを書く. ここでは空だけど例えば [foo:0.1, bar:0.2] ってな感じ. 後ろの数字はバージョン.



def evicts = ['controllers']



このプラグインによって不要になった他のプラグインを記述する (evict とは立ち退き, 強制退去). ここでは組み込みの controllers プラグインが指定されているけど, なぜか依然として controller は使えるようだ... (もしかして evict が正しい?)



def doWithSpring = {

def applicationClass = application.allClasses.find { Application.class.isAssignableFrom(it) }



if(applicationClass) {

applicationBean(applicationClass)

}

}




doWithSpring では Spring Bean Builder を使って必要な bean を定義するのが普通. しかしここでは何をやっているのだろう? 中身を見てみるといきなり "application" という変数が出てくる. どこにも定義されていない. これはいったい何だ?



今までもいろいろ def したが, このクラスはスーパークラス (extends) があるわけでもインタフェース (implements) があるわけでもない. これらの def が必要なのはひとえに「規約」による. 標語としてはいわゆる「covention over configuration (設定より規約)」というやつだ (実際には config ではないので少し違うが). いずれにしろ, この規約を決めているやつがどこかにいるはずだ. それを探してみよう.



続く...



grails + wicket

grails で wicket と scaffolding された GSP を簡単に両立させられるみたい.



それなりの日記


これは嬉しい. だってマスタ・メンテ用のアプリは scaffold されたものにちょっと手を入れればいい (現状ではちと厳しいのも事実だが:-) し, エンド・ユーザ用のアプリはちゃんとデザイナに HTML を作ってもらえるわけだ.



素晴らし.

2007年5月25日金曜日

grails でルール

さて, 今度は grails でルール・エンジンが使えるか.



例えば drools (今は JBoss Rules とか言うのか? 嫌な感じだ:-). rete アルゴリズムが載っていてオープンと言えば drools. groovy くらいの記述力があれば, ちょっとしたビジネス・ロジックだったら別にルール・エンジンを動かすこともない. ルール・エンジンの出番はもうこんがらがって人手に負えない, というようなときだから, エンジンそのものや環境がちゃんとしているものでないと意味がない. というわけで drools.



なのだが.



drools はルールの集まりを Java と同様「パッケージ」として扱う. 無名のデフォルト・パッケージはない. ルール内で参照するオブジェクトは (同じパッケージでなければ) ちゃんと full qualified name で import しなければならない.



一方 grails は domain class を無名のデフォルト・パッケージに置く. ちょっとやってみたところでは, 好き勝手なパッケージには置けない (Spring のエラーになる).



ということで, grails の domain class に drools からアクセスできないのだな. 意味ない.



grails で適当なパッケージに置けるようにするためにはどうすればいいか, まだ見てないけど, うーん, 「ちょっとしたこと」ではなさそうだなぁ.



続きはまた書く.



2007年5月19日土曜日

grails でワークフロー

grails で jbpm が使えるかどうか, まずは試してみる. jbpm を選んだのは, 前にワークフロー・エンジンを使うときに一番まともそうで, これを使ったから.

grails でワークフローを扱おうと思ったら, とりあえず Java の既存のワークフロー・エンジンを取り込んでしまうのが簡単で (これが grails のいいところ), 例えば jbpm だとこんな感じになる.



まずは <project>/lib/ に jbpm の jbpm-jpdl.jar を放り込む.



次に, jbpm では残念ながら "def" というパッケージ名を多用していて, これは groovy では当然キーワードなので, 文法的に許してもらえない. 仕方がないので, (とりあえず最小限必要な) 次のようなラッパを作って,



package jp.co.metabolics.jbpm.grails;



public class ProcessDefinition extends org.jbpm.graph.def.ProcessDefinition {

public static org.jbpm.graph.def.ProcessDefinition parseXmlString(String xml) {

return org.jbpm.graph.def.ProcessDefinition.parseXmlString(xml);

}



}


これをjar にして, 同じく <project>/lib/ に放り込んでおく. これだけ.



jbpm のユーザ・ガイドにある最初の例題を groovy で書いてみよう.







MarkupBuilder のおかげでこんな感じに書ける.



import jp.co.metabolics.jbpm.grails.ProcessDefinition

import org.jbpm.graph.exe.ProcessInstance

import org.jbpm.graph.exe.Token



void testHelloWorldProcess() {

// ここでワークフローを定義している.

def writer = new StringWriter()

def b = new groovy.xml.MarkupBuilder(writer)

b.'process-definition' {

b.'start-state' { transition(to:'s') }

state(name:'s') { transition(to:'end') }

b.'end-state'(name:'end')

}

def processDefinition = ProcessDefinition.parseXmlString(writer.toString())

assert processDefinition

println processDefinition



// プロセス・インスタンスを作る

def processInstance = new ProcessInstance(processDefinition);

assert processInstance

println processInstance



// トークンを得る

def token = processInstance.getRootToken();

assert token

println token.getNode()



assertSame(processDefinition.getStartState(), token.getNode());



// 開始

token.signal();

assertSame(processDefinition.getNode("s"), token.getNode());

println token.getNode()



// 次のノードへ

token.signal();

assertSame(processDefinition.getNode("end"), token.getNode());

println token.getNode()

}


jbpm はワークフロー・エンジンとしても, 状態機械としても, BPEL エンジンとしても使える. ワークフローそのものは, eclipse でグラフィカルに描くこともできる. 基本的な使い方は (def パッケージのクラスを使う場合にラッパを挟む以外は) Java の場合と一緒. Java と一緒というのは楽と言えば楽だけど, groovy / grails の強力さを生かし切れていないとも言えるので, できれば, plug-in を作って, builder を提供したり, 必要なメソッドは inject したいところだ.



その辺はまたそのうち.



2007年5月18日金曜日

wicket plug-in

Grails の wicket plug-in が出ている.



http://graemerocher.blogspot.com/2007/05/grails-wicket-wonders-of-grails-plug-in.html



しかも作ったのは Graeme Rocher だ. 20 分で書いたとか言っている:-)



今までの Grails とは controller の構造も少し変わってくるし, scaffolding もされないけど, カスタムなページだけでも HTML だけで (gsp タグを使わずに) 書けるのは嬉しいかもしれない.



2007年5月10日木曜日

日本語 on MacOS X

MacOSX で grails を使っていると, .groovy コード中に埋め込まれた日本語が web ブラウザ上で化ける. 例えば

static constraints = {

attendance(inList:['未定', '出席', '欠席'])

......

}


とすると, 生成される GRUD アプリケーションで attendance はセレクトボックスで表示されるのだけど, 項目名が読めない.


ま, ここにこういうコードが埋め込まれているのがいいかどうかという議論はあり得るわけだが, それはそれとして.


MacOSX だと, Java のデフォルトのエンコーディングが多分 sjis みたいのになっているから, ${GRAILS_HOME}/bin/startGrails というシェル・スクリプトの適当な場所 (前の方) に,

JAVA_OPTS="-Dfile.encoding=utf-8"

を付け加えておけばOK.


もちろん, 元の .groovy コードは utf-8 で書いておかねばだけど.


.gsp ファイル中の日本語は (utf-8で書いておけば) 大丈夫だし, g:message タグを使えば i18n できる.



many-to-many mapping

[0.5 のリリースノートには出ていなかったけど, 関連クラスのようなものや Hibernate の マップ・ファイルを書かずに多対多のマッピングが GORM で可能になった模様.



まだ試してない.



http://www.nabble.com/Many-to-Many-Mapping-without-Hibernate-XML-tf3719083.html



ビジネス・ロジックの置き所

いわゆる「ビジネス・ロジック」と呼ばれているものがある. ドメイン知識の一部だけれど, 「処理」的なもの, 複数のドメイン・クラスにまたがる何かを指している.



grails ではビジネス・ロジックは service として実装しなさい, というのが基本だ. domain class には書かない. そして service は controller が呼び出すもので, domain class が呼び出すものではない. これは grails に限らず, 最近の実装法はこの方向にある. domain class という名前ではあるけれど, 実態は DTO (Data Transfer Object) に過ぎない. そして DAO (Data Access Object) を通してアクセスする. しかし, そもそも DTO とか DAO とかいう名前, 存在そのものはオブジェクト指向としてどうなのよ? 気持ち悪いなぁ.



元々オブジェクトは現実世界の何かに関する知識を表すものだった. ドメイン・クラスとか概念オブジェクトとかは, その中でも問題ドメインに関する知識を表すオブジェクトだ. でも DTO とか DAO とかって解決領域に関するものでしょう? 実装依り過ぎる. それは裏に隠れている (別のドメインである) べきなんじゃないの?, というのが僕の, 実行可能知識の立場.



例えばあるメーカが小売店に対して報奨金を出している. 前期の売上額の範囲に応じて, 報奨金の割合が変わるとする. もちろんこの報奨金のルールの内容自体は不安定なもの (人間の恣意によって変わりやすい) ものだから, 「ルール」として抽出してよい (するべき). でも, このルールによって計算される報奨金というものがあると言うことは「ドメイン・クラス」の中に書かれているべき.



むろん, 昨今の実装上の問題として, 永続化層がオブジェクトをそのまま保存できない場合が多いので, この「ルール」がいつ起動されるべきか難しいわけだが...



続きはまた書く.



2007年5月9日水曜日

なぜ grails か

grails は普通には CRUD 生成フレームワークと思われているわけだが, 僕にとってはそれはどうでもいい (いや, もちろん CRUD アプリケーションが生成されることはいいことには違いないんだけど).



ここで grails を扱っている理由は何よりも「モデル駆動開発環境」としてだ. それも「日常会話的モデル駆動開発」環境.



モデル駆動開発のツールもいっぱいある. けど, 日常会話ができない. ものすごくしゃっちょこばって, ぎごちなく, 難しく, 辞書を引き引き, 話さないと伝わらない.



例えば BridgePoint. 高い. まずそれだけで駄目だ. 舌がつっちゃう.



例えば OptimalJ のたぐい. ださい. 会話が弾まない. 本当に嬉しいのかどうか分からない.



例えば eclipse の Modeling Project. 頑張っているとは思うけど, まだまだ発展途上だし (いつ発展が終わるの?), これを使うのは大変だ. プログラムを書くのと同じくらいモデルを書くのが大変だ. それじゃあ意味ないよなぁ.



もっと自然に会話したいわけだ. 自分の気持ちを素直に伝えたい. プログラマに馴染みのある例で言えば, emacs のような. grails にはその可能性があるんじゃないか, と思った. grails では普通は UML のような絵は書かないけど, 一見ふつうのプログラミング言語みたいだけど, 実はモデルを書いているわけだね (そう思い込もうとしている).



特に Groovy の Builder で簡単に内部 DSL を作ることができる. Groovy の MOP (Meta Object Protocol) で上位の知識を形式化 / モジュール化できる. この二つはモデリング言語として強力.



grails は emacs と同じで「環境」だから, 生の状態でも使えることは使えるけど, その真価は plugin にある. 自分たちのノウハウは plugin の形で追加され, grails は進化していく.



なぜ Ruby on Rails じゃないか. R on R はみんなにとってもクールだって評判がいいから (僕はへそ曲がりだからね). schemaから始まるのは趣味じゃない. Groovy は Java VM の上で走る. 「Java の一種です」と言えば保守的な人でも騙せる. Java の膨大な資産を使える. もちろんあまりイカしてないのも多いわけだけど, それは裏に隠れているから構わない. それよりも「立っている者は親でも使え」だ (ちょっと意味が違う気もするが).



eclipse からの run

今までは emacs でやっていたので, これが 0.5 になってからの現象かどうかは分からないのだけれど, eclipse で Run | Run... -> Java Application -> <project> でプロジェクトを起動すると, ダイナミックなビューを scaffold しようとして落ちる. テンプレートが (あると思いこんでいる場所に) 無いと言うのだが, うーむ. なぜだ.



% grails dev package



はやってある. テンプレートは



${basedir}/web-app/WEB-INF/templates/scaffolding/


にはあって, % grails dev run-app しているときはこれで大丈夫なのだが, eclipse からだとなぜかまず



${basedir}/src/templates/scaffolding/



を, それがなければ (development 環境では普通はない)



${grailsHome}/src/grails/templates/scaffolding/




を見に行く. ところが, このコード中で ant の環境から GRAILS_HOME を取り損なっているようで {grailsHome} が null なのだ. 仕方がないので, ${basedir}/src/templates/scaffolding/ にテンプレートをコピーすることにする.



% grails package



(dev なし) しても多分いいはず.



0.5 で test が error

0.5 に移行してから % grails test-app すると,



Error running tests: org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [Foo#1]



などと言われることがある. テストの中でデータベースを更新したりしている場合ね.



こんなときには ${GRAILS_HOME}/scripts/TestApp.groovy の 162 ~164 行目



app.domainClasses.each { dc ->

dc.clazz.executeUpdate("delete from ${dc.clazz.name}")

}




をコメントアウトすると, とりあえず上記のエラーは出なくなるはず. このコードはテストメソッド終了時にデータベースをクリーンアップしているのだけど, 消すには消す順序ってぇものがあるわけで, それを無視して適当に消しているのがまずいわけだ.



一方でこの修正を入れるとクリーンアップを自動でしなくなるので, データベースの中身は tearDown() 辺りでちゃんと消しておかないと, 前のテスト・メソッドの影響が残ってしまうということになるのじゃね.



http://www.nabble.com/Unit-tests-and-GORM-%28Hibernate%29-tf3697726.html